JavaScript 这门语言跟其他语言有区别,如果你看Jquery,Vue,React,Angular源码的时候,如果你没有理解事件机制很难理解里面的一些内容。
JAVA ,Python是多线程语言(Python可是说是伪多线程)代码的写法跟JavaScript有太大的区别了,写惯多线程语言的代码,写JavaScript要有一定的适应期。
现在HTML5,ES6,ES7,ES8,ES9有很大的改进很多时候不用写这种回调的代码了JavaScript有点像Python了现在。
先不要理什么是任务,看一段代码;
for(var i=0;i<5;i++ ){
setTimeout(function(){
console.log(i)
},0)
}
// 请问打印出是什么
答案 5
js 是就是这么奇葩,每个前端开发者都会被这个异步回调搞的头晕脑胀。
下面这段代码打印顺序是什么呢?
console.log('start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('end');
![JS事件循环机制(event loop),什么是宏任务、微任务](http://p2.ttnews.xyz/loading.gif)
答案是:
- start
- end
- promise1
- promise2
- setTimeout
其实有些浏览器是不支持Promise 答案会有所不同。但是那些不是本篇文章考虑的内容。
为什么会出现这样的顺序呢?
要想知道为啥会这样那就要理解JavaScript的宏任务和微任务。
js执行的时候也就是每个线程(不知道没有关系)都有一个事件队列,这个事件队列是等所有的主线程执行(原代码的顺序)完成后,再执行事件队列执行的走的是事件循环,而且遵循事件循环。
这些宏任务源保证了在本任务源内的顺序。但是浏览器每次都会选择一个源中的一个宏任务去执行。这保证了浏览器给与一些宏任务(如用户输入)以更高的优先级。好的,跟着我继续……
宏任务(task)
浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->...)
在上面的例子中:
setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。
这就是为什么打印‘setTimeout’在‘end’之后,即使setTimeout代码再‘end’前面。因为打印‘end’是第一个宏任务里面的事情,setTimeout它的回调产生一个新的宏任务,所以会在下一个事件循环的时候执行,即使是为0秒也是在‘end’之后执行promise1,promise2,在下一个
微任务(Microtasks )
微任务是在当前宏任务 task 执行结束后立即执行的任务。
需要异步的执行任务而又不需要分配一个新的宏任务 task。
这样便可以减小一点性能的开销。
只要执行栈中没有其他的js代码正在执行且每个宏任务执行完,微任务队列会立即执行。
微任务包括了mutation observe的回调还有接下来的例子promise的回调。
知道了宏任务 (task)和微任务(Microtasks ),再来分析上面的代码。
代码执行的时候这个时候是第一个宏任务(task),执行 start ,setTimeout生成另外一个宏任务,也就代码挂起来了等下个事件循环的时候执行。执行Promise的时候是一个微任务,所以是等到当前宏任务执行完在走,所以执行 end 再执行微任务promise1,promise2。这个时候第一个宏任务执行完毕,第二个宏任务开始执行setTimeout。
翻译原文:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly
这篇文章希望能帮助到各位同学。
閱讀更多 閣主無名 的文章