原文:https://www.jianshu.com/p/fb1c07b4d90d
原文:http://www.sohu.com/a/294185046_120045139
一、单线程和任务队列
setTimeout(() => { task() },3000) sleep(10000000)
代码执行过程:
task()进入Event Table并注册,计时开始。
执行sleep函数,很慢,非常慢,计时仍在继续。
3秒到了,计时事件timeout完成,task()进入Event Queue,但是sleep也太慢了吧,还没执行完,只好等着。
sleep终于执行完了,task()终于从Event Queue进入了主线程执行。
setTimeout这个函数,有时真正的延迟时间远远大于3秒。setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。
对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。
唯一需要注意的一点是,对于setInterval(fn,ms)来说,每过ms秒,会有fn进入Event Queue。一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。
一、宏任务与微任务
除了广义的同步任务和异步任务,我们对任务有更精细的定义:
macro-task(宏任务):包括整体代码,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick(promise的回调then()是微任务)
主线程里立即执行的是:new promise, promise 语句, console.log 按出现的先后执行
立即执行的执行完后执行同一个宏任务里的微任务队列,微任务依次执行完毕后再继续下一个宏任务。
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。
setTimeout(function() { console.log(‘setTimeout‘); }); new Promise(function(resolve) { console.log(‘promise‘); resolve() }).then(function() { console.log(‘then‘); }); console.log(‘console‘); 输出结果:promise;console;then; setTimeout
console.log(‘1‘); setTimeout(function() { console.log(‘2‘); process.nextTick(function() { console.log(‘3‘); }); new Promise(function(resolve) { console.log(‘4‘); resolve(); }).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‘); resolve(); }).then(function() { console.log(‘12‘) }) })
输出结果:1,7,6,8,2,4,3,5,9,11,10,12。
事件循环Event Loop:事件循环是js实现异步的一种方法,也是js的执行机制。
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在任务队列中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取“任务队列”,依次执行那些事件,所对应的回调函数。
执行栈中的代码(同步任务),总是在读取“任务队列”(异步任务)之前执行,请看下边的例子:
var req = new XMLHttpRequest();
req.open(‘GET‘, url);
req.onload = function (){};
req.onerror = function (){};
req.send();
上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取"任务队列"。所以,它与下面的写法等价。
var req = new XMLHttpRequest();
req.open(‘GET‘, url);
req.send();
req.onload = function (){};
req.onerror = function (){};
也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取"任务队列"。
console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);
上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。
如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。
setTimeout(function(){console.log(1);}, 0);
console.log(2);
上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。
原文:https://www.cnblogs.com/minyDong/p/11499015.html