js--事件循环

178 阅读5分钟

事件循环我到现在都没有弄清楚,不过慢慢地有一点儿眉目了.能够根据代码判断出执行结果了.后面再慢慢消化吧.

 console.log('1');  
 setTimeout(function() {          
     console.log('2');                
     new Promise(function(resolve) {                  
         console.log('4');                  
         resolve();
      }).then(function() { 
         console.log('5'); 
      })
  }, 0); 
 new Promise(function(resolve) {  
     console.log('7');  
     resolve();
 }).then(function() { 
 console.log('8'); 
 });
 new Promise(function(resolve) {
      console.log('13'); 
      resolve(); 
  }).then(function() { 
     console.log('14');
  });
  setTimeout(function() {
      console.log('9'); 
      new Promise(function(resolve) { 
          console.log('11');  
          resolve();
      }).then(function() {
           console.log('12'); 
      }); 
   }, 0);


      以上代码的运行结果是 1, 7, 13, 8, 14, 2, 4, 5, 9, 11, 12

所以根据运行结果去分析,console直接运行,promise也是直接运行,promise后面的then会放在某一个任务队列(应该是微任务),setTimeout是宏任务,轮到某一宏任务执行时,必然会把该宏任务中的所有任务执行完成才会执行下个宏任务.队列是先进先出所以在同一类队列中,先放入的先执行.

另外一道题:

console.log('1');

setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => {
    console.log('3');
  })
  new Promise((resolve) => {
    console.log('4');
    resolve();
  }).then(() => {
    console.log('5')
  })
})

Promise.reject().then(() => {
  console.log('13');
}, () => {
  console.log('12');
})

new Promise((resolve) => {
  console.log('7');
  resolve();
}).then(() => {
  console.log('8')
})

setTimeout(() => {
  console.log('9');
  Promise.resolve().then(() => {
    console.log('10');
  })
  new Promise((resolve) => {
    console.log('11');
    resolve();
  }).then(() => {
    console.log('12')
  })
})

答案:1,  7, 12,  8,  2 ,4 ,3 , 5 ,9 ,11 ,10, 12

从这题可以总结: 每一个setTimeout模块儿完成后才会执行其他模块儿

以下两道题如果不仔细很容易弄错

    var promise1 = () => (
        new Promise( resolve => {
            console.log(1);
            var promise2 = new Promise( resolve => {
                console.log(2);
                setTimeout( ()=>{
                    console.log(3);
                    resolve();
                },0);
             });
             resolve()
             promise2().then( ()=> {
                  console.log(4);
             });
       })
  
   );
   promise1().then( () => {
       console.log(5);
   });
   console.log(6);

答案: 1, 2, 6, 5, 3

var promise1 = () => (
    new Promise( resolve => {
        console.log(1);
        var promise2 = () => ( 
            new Promise( resolve => {
                console.log(2);
                setTimeout( ()=>{
                    console.log(3);
                    resolve();//promise2后面的then方法得在执行这个宏任务时才放到微任务中;若这行代码和上一行代码交换位置输出结果一样,原因:打印3立即执行而打印4放在了微任务队列
                },0);
             })
         );
         resolve()
         promise2().then( ()=> {
             console.log(4);
         });
    }) 
);
promise1().then( () => {
    console.log(5);
});
console.log(6);

答案: 1, 2, 6, 5, 3, 4

以上两道题的主要区别在于promise2是一个函数还是一个promise对象.是一个对象时promise2()就会出错,所以紧随其后的then不会执行.  

为什么5在 3和4的前面?调用promise1()的时候其后的then方法就放在微任务队列里面了吗?接着与下面这一题进行对比:

var promise1 = () => (
    new Promise( resolve => {
        console.log(1);
        var promise2 = () => ( 
            new Promise( resolve => {
                console.log(2);
                resolve();
             })
         );
         resolve()
         promise2().then( ()=> {
             console.log(4);
         });
    }) 
);
promise1().then( () => {
    console.log(5);
});
console.log(6);

答案: 1, 2, 6 , 4, 5

如果按照假设: 调用promise1()后,它的then即刻放入微任务队列,那这题的答案就不是这样的,所以假设不成立. 应该是创建的promise对象完成(无论是是rejected还是resolved)后其后的then方法才放入微任务队列中. Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。通过下面这题就能得出答案: resolve()调用的位置是影响5被打印的时机的关键.

var promise1 = () => (
    new Promise( resolve => {
        console.log(1);
        var promise2 = () => ( 
            new Promise( resolve => {
                console.log(2);
                setTimeout( ()=>{
                    console.log(3);
                },0);
                resolve()
             })
         );
         resolve();//此时promise1后面的then方法就放入到微任务队列了
         promise2().then( ()=> {
             console.log(4);
         });
    }) 
);
promise1().then( () => {
    console.log(5);
});
console.log(6);

答案: 1, 2, 6, 4, 5, 3.

有async/await的事件循环:

console.log('script start');
setTimeout(() => {  
    console.log('北歌');
}, 1 * 2000);
Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() { 
    console.log('promise2');
});
async function foo() {  
    await bar()  
    console.log('async1 end')
}
foo()
function bar() { 
    console.log('async2 end')
}
async function boo() {  
    await baz()    
    console.log('async3 end')
}  
boo()
function baz() {  
    console.log('async4 end')
}
console.log('script end');

运行得到的结果: script start      async2 end      async4 end      script end     promise1     async1 end       async3 end      promise2       北歌

对async/await不太懂的,可以先看下面这个例子

async function foo() {   
    await bar();    
    await zar();   
    console.log('await')
}
function bar() {  
    console.log('bar')
}
function zar() {   
    console.log('zar')
}
console.log('1')
foo();
console.log('2')

运行结果: 1      bar      2        zar      await

注意:await后的是同步任务还是异步任务

async f1(testuid: string) {        await f1();//f1为同步任务        await console.log('llll') }
// 执行完f1时再打印'llll',如果f1是异步则f1依然会后执行

从这里例子中可以得出: 只有第一await 后面的函数会被立刻执行,其他的都会放入微任务队列中(与promise.then里面的执行代码处于同一个队列),等待异步函数foo()后面的所有同步任务执行完毕才会去执行微任务队列中的任务.

async/await 中当出现错误时的执行顺序

console.log('script start');
setTimeout(() => {  
    console.log('北歌');
}, 1 * 2000);
Promise.resolve().then(function() {
   console.log('promise1');
}).then(function() { 
    console.log('promise2');
});
async function foo() {  
    await bar()  
    console.log('async1 end')
}
foo()
function bar() {    
    console.log('async2 end') 
}
async function errorFunc () {  
    try {    
        await Promise.reject('error')  
    } catch(e) {   
        console.log(e)  
     }  
     console.log('async1');  
     return Promise.resolve('async1 success')
 }
 errorFunc().then(res => 
     console.log(res)
 )
console.log('script end');

其执行结果: script start     async2 end     script end    promise1    async1 end     promise2          error        async1         async1 success         北歌

与前面一个例子对比能够总结到:  到async /await 中出现错误时,整个代码块涉及到的内容都会放在微任务队列的后面(等其他微任务执行完以后才执行).当然,还是要比宏任务先执行.即使把foo()放在 errorFunc()函数的后面,它也在在所有的微任务执行完后才会执行.

此处出错的原因是 代码Promise.reject('error')会报错,而正确书写的形式如下:

Promise.reject('error').then(()=>{}, ()=> {console.log('reject')});

返回的promise的状态是rejected,所以会输出'reject'

async/await更加全面的例子

输出结果: '1', bar, '2', 1, zar, 3, await

可以得出这个例子的具体执行过程是:
同步代码直接运行('1');异步函数foo()先执行await bar,输出'bar'; 现在只将await zar()放入微任务队列中(下面的同步代码并没有放入微任务队列); 运行同步代码,输出'2';  promise立即执行,输出1; 因为成功所以将console.log(3)放入微任务队列;第一次轮询结束,进入下一个轮询;  微任务的依次执行,输出'zar';将console.log('await')放入微任务队列; 输出3; 输出await;微任务队列也执行完毕.宏任务队列为空;整个执行结束;

await bar()执行完将 await zar()放入微任务队列; await zar()执行完才会将console.log('await')这个同步代码放入微任务队列. 而不是await bar()执行完后将后面的所有一次性放入微任务队列

Promise.resolve()是微任务

setTimeout(
   ()=> console.log(4)
, 100);Promise.resolve()
.then(()=> console.log('1'))
.then(()=> console.log('2'));  

console.log('3'); 

输出结果为: 3, 1, 2, 4. 可见Promise.resolve()是微任务

文中如有错误,欢迎指正.相互学习!