如有错误欢迎指正,谢~
我们都知道js是单线程语言,也就是只有一个线程,常称为主线程,在该线程中包含任务队列和执行栈
任务队列:
一些异步操作会将相关回调添加到任务队列中,而且不同的异步操作添加到队列的时间也不一样。
eg:
①onclick 等异步是当事件触发立即会将回调放到任务队列中。
②settimeout 会等到延迟时间到了再把回调放到任务队列中。
③像网络请求ajax是等到请求完成的时候再把回调放到任务队列中去。
执行栈:
执行栈就相当于一个全局方法,主方法,里面有个同步任务和异步任务,主线程在执行碰到同步任务就执行,碰到异步任务就将其回调放到任务队列中,执行完同步任务之后,就开始执行任务队列了,有一点要知道:
①一个事件循环中有一个或多个任务队列。
②每个事件循环都有一个microtask队列。
③macrotask队列就是我们常说的任务队列,microtask队列不是任务队列
④一个任务可以被放入到macrotask队列,也可以放入microtask队列
具体怎么划分看下面:
macrotasks(就是我们平常说的任务队列)和microtasks的划分:
macrotasks包括:script (整体代码),setTimeout,setInterval,setImmediate,requestAnimationFrame,I/O,UI rendering
microtasks包括:process.nextTick,Promises,Object.observe,MutationObserver
那么在执行队列(macrotasks队列==任务队列和microtasks队列)的时候,是怎样执行的呢?
microtask执行会在以下两种情况:
①任务队列(macrotask ==任务对列)回调后执行,前提条件是当前没有其他执行中的代码。
②每个任务队列(macrotask ==任务对列)末尾执行。
另外在处理microtask期间,如果有新添加的microtasks,也会被添加到队列的末尾并执行。
可以这样理解:一切的同步执行任务都是宏观的任务,也就是说宏观队列中包含微观队列
可以总结执行顺序如下:
开始 ----->执行所有同步代码---->取微观任务队列的全部任务依次---->取任务队列第一个task执行 ----->取microtask全部任务依次执行 ----->取任务队列下一个任务执行----->再次取出microtask全部任务执行-----> 。。。这样循环往复直到两个任务队列全部执行完
伪代码大概表示了这个过程:
1 //js的任务队列和执行栈 2 let queue=[]; 3 let mircqueue=[]; 4 function Main(){//浏览器js主线程 5 let event; 6 function executeStack(){//执行栈 7 ..............;//执行栈中执行的同步代码 碰到宏观任务将回调放到任务队列中 下一次循环会执行 8 //执行完可执行的任务之后,立即执行微观任务,如下: 9 for(var i=0;i<mircqueue.length;i++){//取出所有microtask执行 10 event=queue.shift();//取出任务队列一个任务 11 event();//执行 12 } 13 while(true){//事件循环 14 if(queue.length>0){ 15 event=queue.shift();//取出任务队列一个任务 16 event();执行 17 } 18 if(mircqueue.length>0){ 19 for(var i=0;i<mircqueue.length;i++){//取出所有microtask执行 20 event=queue.shift();//取出任务队列一个任务 21 event();//执行 22 } 23 } 24 25 } 26 } 27 } 28 29 function Other(){//浏览器其他的线程 30 function onxxx(){ 31 queue.push(new Task());//浏览器线程往任务队列中添加回调事件 32 //或者 33 mircqueue.push(new Task());//浏览器线程往microtask中添加回调事件 34 } 35 } 36 Main(); 37 Other();
那么我们来看一个例子:
eg1:
1 setTimeout(function(){ 2 console.log(1) 3 },0); 4 new Promise(function(resolve){ 5 console.log(2) 6 for( var i=100000 ; i>0 ; i-- ){ 7 i==1 && resolve() 8 } 9 console.log(3) 10 }).then(function(){ 11 console.log(4) 12 }); 13 console.log(5);
结果是
2,3,5,4,1
eg2:
1 setTimeout(function(){ 2 console.log(1) 3 },0); 4 setTimeout(function(){ 5 console.log(6) 6 },2000); 7 new Promise(function(resolve){ 8 console.log(2) 9 for( var i=100000 ; i>0 ; i-- ){ 10 i==1 && resolve() 11 } 12 console.log(3) 13 }).then(function(){ 14 console.log(4); 15 setTimeout(function(){ 16 console.log(8) 17 },1000) 18 }); 19 console.log(5);
这个执行结果是啥呢???想想
结果是:
2,3,5,4,1,8,6
eg3:
setImmediate(function(){ console.log(1); },0); setTimeout(function(){ console.log(2); },0); new Promise(function(resolve){ console.log(3); resolve(); console.log(4); }).then(function(){ console.log(5); }); console.log(6); process.nextTick(function(){ console.log(7); }); console.log(8);
node 环境下:
3,4,6,8,5,1,2,7
浏览器环境下:
settimeout 优先级大于 setImmediate
nextTick 优先级 大于 promise
3,4,6,8,7,5,2,1
eg4:
//加入两个nextTick的回调函数 process.nextTick(function () { console.log('nextTick延迟执行1'); }); process.nextTick(function () { console.log('nextTick延迟执行2'); }); // 加入两个setImmediate()的回调函数 setImmediate(function () { console.log('setImmediate延迟执行1'); // 进入下次循环 process.nextTick(function () { console.log('强势插入'); }); }); setImmediate(function () { console.log('setImmediate延迟执行2'); }); console.log('正常执行');
node 环境下:正常执行,setImmediate延迟执行1,setImmediate延迟执行2,nextTick延迟执行1,nextTick延迟执行2,强势插入
浏览器环境下:正常执行,nextTick延迟执行1,nextTick延迟执行2,setImmediate延迟执行1,强势插入,setImmediate延迟执行2
总结一下,便于自己学习,也分享大家一起学习。(#^.^#)