• 关于setTimeout和Promise执行顺序问题


    先看一段代码

    console.log('打印'+1);
    setTimeout(function(){
        console.log('打印'+2);
    })
    new Promise(function(resolve,reject){
            console.log('打印'+3);
          }).then(
      console.log('打印'+4));;
    console.log('打印'+10);
    new Promise(function(resolve,reject){
          setTimeout(function () {
            console.log('打印'+5);
          });
      }).then(
      console.log('打印'+6));
    setTimeout(function(){
        new Promise(function(resolve,reject){
            console.log('打印'+7);
          });
    })

    执行结果:

    console.log('打印'+1);
    setTimeout(function(){
        console.log('打印'+2);
    })
    new Promise(function(resolve){
            console.log('打印'+3);
            resolve();
          }).then(function(){
            console.log(4);
          }
      );
    console.log('打印'+10);
    new Promise(function(resolve){
          setTimeout(function () {
            console.log('打印'+5);
          });
          resolve();
      }).then(function(){
    
      console.log('打印'+6)});
    setTimeout(function(){
        new Promise(function(resolve){
            console.log('打印'+7);
          });
    })
    //执行结果:
    //1;3;10;4;6;2;5;7

    可以看出Promise比setTimeout()先执行。

    因为Promise定义之后便会立即执行,其后的.then()是异步里面的微任务。

    而setTimeout()是异步的宏任务。

    引自https://www.cnblogs.com/woodyblog/p/6061671.html 

    js是单线程语言,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。

    浏览器

    js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。

    任务队列

    刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器

    setTimeout(function(){
        console.log(time is out);
    },1000);

    执行这段代码的时候,浏览器异步执行计时操作,当1000ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。
    所以说,js是一直是单线程的,浏览器才是实现异步的那个家伙。

    导图要表达的内容用文字来表述的话:

    • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
    • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

     

    主线程

    js一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。下面我们来进行更深一步的理解。

    (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

    (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

    (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

    (4)主线程不断重复上面的第三步。

    只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

    Event Loop

    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

    为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)。

    上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

    异步任务有宏任务和微任务。

    2.宏任务macrotask:

    (事件队列中的每一个事件都是一个macrotask)

    优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval

    比如:setImmediate指定的回调函数,总是排在setTimeout前面

    3.微任务包括:

    优先级:process.nextTick > Promise > MutationObserver

    下面这个代码输出结果是什么?

    主程序和和settimeout都是宏任务,两个promise是微任务

    第一个宏任务(主程序)执行完,执行全部的微任务(两个promise),再执行下一个宏任务(settimeout),所以结果为:

    执行结果:

  • 相关阅读:
    将已排序的数组乱序
    Roadmap!!
    测试
    最大对称字串
    约瑟夫环问题
    大家好
    MYSQL数据库导入SQL文件出现乱码如何解决
    Hibernate缓存
    Spring备忘四(涵盖Spring2.5)
    Struts2 Hello,Wold
  • 原文地址:https://www.cnblogs.com/sunmarvell/p/9564815.html
Copyright © 2020-2023  润新知