zoukankan      html  css  js  c++  java
  • js基础-node环境下说哈js异步,同步,单线程,微任务和宏任务

    js执行环境是‘单线程’ 

    什么是单线程(single thread)?

    指一次只能完成一件任务。

    如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推

    粗暴理解:【某工厂只有一条生产流水线,做啥都的一个一个排着弄】

     

    好处:实现起来比较简单,执行环境相对单纯;

    坏处:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

    常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

     

    为了解决单线程这问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)

     

    同步模式:

    1)前一个任务完成后,执行下一个任务。

    2)程序的执行顺序与任务的排列顺序是一致的、同步的;

    粗暴理解:【同一时间之内干一件事】

     

    异步模式:

    1)前一个任务结束后,不是执行后一个任务,而是执行回调函数,

         后一个任务不等前一个任务结束就执行

    2)程序的执行顺序与任务的排列顺序是不一致的、异步的

    粗暴理解:【同一时间,因为某事执行需要时间,就空出来先干别的事】

     

    简言之:同步会堵塞代码执行,而异步不会。

     

    微任务和宏任务概念

    1. 宏任务:当前调用栈中执行的代码成为宏任务。 

    2.微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。

    3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

    微任务和宏任务运行机制

    1. 在执行栈中执行一个宏任务。 

    2. 执行过程中遇到微任务,将微任务添加到微任务队列中。

    3. 当前宏任务执行完毕,立即执行微任务队列中的任务。 

    4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。 

    5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。

    微任务:process.nextTick、MutationObserver、Promise.then、process.nextTick、 catch finally

    宏任务:I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame  、new Promise、async

    process.nextTick() > Promise.then() > setTimeout > setImmediate;

    运行方式:先同步任务 再异步任务!

         先宏任务  再微任务!      

    【同步任务时,先执行同步宏任务再执行同步微任务,同步操作完成后,按照先后顺序执行异步操作,先执行异步宏任务再执行异步微任务,若再遇到异步或微任务就抛出就抛出】

    注:Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,

       async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回(如返回awaite紧跟的函数),然后再阻塞

    例子1:

     1 setTimeout(function() {
     2     console.log('setTimeout');
     3 })
     4 
     5 new Promise(function(resolve) {
     6     console.log('promise');
     7     //resolve();
     8 }).then(function() {
     9     console.log('then');
    10 })
    11 
    12 console.log('console');

     结果:promise console setTimeout

    说明:

    1 整段代码作为宏任务进入主线程
    2 遇到settimeout,将其回调函数注册后分发到宏任务Event Queue。
    3 遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue
    4 遇到console.log(),立即执行
    5 第一个宏任务执行结束,看看有什么微任务,发现有then,执行 
    6 第二轮循环,发现宏任务settimeout的回调函数,执行。
    7 结束。

    例子2:

     console.log("1"); //第一轮主线程【1】 
    
          setTimeout(function() {
            //碰到set异步,丢入宏任务队列【set1】:我将它命名为set1
            console.log("2"); //第二轮宏任务执行,输出【2】
            process.nextTick(function() {
              //第二轮宏任务执行,碰到process,丢入微任务队列,【3】
              console.log("3");
            });
            new Promise(function(resolve) {
              //第二轮宏任务执行,输出【2,4】
              console.log("4");
              resolve();
            }).then(function() {
              console.log("5"); //第二轮宏任务执行,碰到then丢入微任务队列,【3,5】
            });
          });
          process.nextTick(function() {
            //碰到process,丢入微任务队列【6】
            console.log("6"); //第一轮微任务执行
          });
          new Promise(function(resolve) {
            console.log("7"); //new的同时执行代码,第一轮主线程此时输出【1,7】
            resolve();
          }).then(function() {
            console.log("8"); //第一轮主线程中promise的then丢入微任务队列,此时微任务队列为【6,8】。当第一轮微任务执行,顺序输出【6,8】
          });
    
          setTimeout(function() {
            //碰到set异步丢入宏任务队列,此时宏任务队列【set1.set2】:我将它命名为set2
            console.log("9"); //第三轮宏任务执行,输出【9】
            process.nextTick(function() {
              //第三轮宏中执行过程中添加到微任务【10】
              console.log("10");
            });
            new Promise(function(resolve) {
              console.log("11"); //第三轮宏任务执行,宏任务累计输出【9,11】
              resolve();
            }).then(function() {
              console.log("12"); //第三轮宏中执行过程中添加到微任务【10,12】
            });
          });
    结果:1,7,8,2,4,5,6,9,11,12,3,10

    例子3:例子2稍微修改

          console.log("1");
    
          setTimeout(function() {
            console.log("2");
            process.nextTick(function() {
              console.log("3");
            });
            new Promise(function(resolve) {
              console.log("4");
            }).then(function() {
              console.log("5");
            });
          });
    
          process.nextTick(function() {
            console.log("6");
          });
    
          new Promise(function(resolve) {
            console.log("7");
            resolve();
          }).then(function() {
            console.log("8");
          });
    
          setTimeout(function() {
            console.log("9");
            process.nextTick(function() {
              console.log("10");
            });
            new Promise(function(resolve) {
              console.log("11");
            }).then(function() {
              console.log("12");
            });
          });
    结果 :1,7,8,2,4,6,9,11,3,10

    关于 async awite 推荐别人写的
    https://blog.csdn.net/VhWfR2u02Q/article/details/84948640

    同步场景

    如:alert()  prompt()

     

    **前端使用异步场景

    在可能发生等待的情况

      1).定时任务:setTimeout setInterval

      2).网络请求:ajax请求,动态<img>加载

      3).事件绑定:addEventListener

    例子1:

    1     console.log(100);
    2     setTimeout(function(){
    3       console.log(200);
    4     },1000);
    5     console.log(300);

      结果:100 300 200

    例子2:

    1     console.log('img开始');
    2     var img = document.createElement('img');
    3     img.onload = function(){
    4       console.log('loaded');
    5     }
    6     img.src="/xxx.png";
    7     console.log('img结束');

         结果:img开始  img结束  loaded

    例子3:

    1     console.log('事件开始!');
    2     var btn1 = document.getElementById('btn1');
    3     btn1.addEventListener('click',function(){
    4       console.log('你点击我了');//点击了才会显示
    5     })
    6     console.log('事件结束!!');

      结果:事件开始!  事件结束! 你点击我了

    例子4:

    1     console.log(1);
    2     setTimeout(function(){
    3       console.log(2);
    4     },1500);
    5     console.log(3);
    6     setTimeout(function(){
    7       console.log(4);
    8     },300);
    9     console.log(5);

      结果:   1 3 5 4 2   //根据封禁时间解封

     

  • 相关阅读:
    SQL优化,解决系统运行效率瓶颈
    C#中 哪些是值类型 哪些是引用类型
    C#异常类相关总结
    对象 序列化 字节流 传输
    给数组中的每个元素赋值
    对象转化为 xml字符串
    .NET BETWEEN方法
    Datatable To List<Entity>
    ajax原理
    gulp记录
  • 原文地址:https://www.cnblogs.com/lingXie/p/11493925.html
Copyright © 2011-2022 走看看