单线程
JavaScript是基于单线程的,即所有任务都需要排队,前一个任务结束,才会执行后一个任务。这样设计的原因是:JavaScript 在最开始设计的时候,其基本功能就是操作DOM节点,试想,如果一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,那么浏览器将不知道如何工作。
异步模式与事件循环
单线程最大的问题是所有任务都需要排队,如果前一个任务耗时很长,后一个任务就不得不一直等着,因此JavaScript 引入了“异步模式”。"异步模式"将所有任务分成两种:即同步任务和异步任务。同步任务指的是,在主线程上排队执行的任务;异步任务指的是,那些准备执行、被放在"任务队列"中的任务,一旦主线程上的所有同步任务执行完毕,队列中的任务就会结束等待的状态,开始执行。主线程从队列中读取任务的过程是循环不断的,这种运行机制称为Event Loop(事件循环)。
示意图:
例子:
console.log(1);
setTimeout(function() {
console.log(2)
}, 0);
setTimeout(function() {
console.log(3);
}, 10);
console.log(4);
//输出:1 4 2 3
Task 队列 与 Job 队列
到了ES6 的标准,由于出现了 Promise ,ES5 时代的"同步任务"与"异步任务"已经没有办法解释其中的原理,因此出现了 task 队列与 job 队列之分。能够发布异步任务的任务源都属于 task 队列:
event
、ajax(XMLHttpRequest)
、setTimeout / setInterval
都会加入task 队列中,主线程可以发布 Promise 的异步任务,因此也属于 task 队列。而 job 队列也可以称为 callback 队列,主要用来存放由任务源分配的回调函数。
下面通过例子进行详细讲解:
例子:
<button>OK</button>
console.log(1);
setTimeout(function() {
console.log(2);
}, 0)
new Promise(function(resolve) {
console.log(3);
for (var i = 0; i < 1000; i++) {
if (i = 999) {
resolve();
}
}
console.log(4);
}).then(function() {
console.log(5);
})
document.querySelector("button").onclick = function() {
console.log(6);
}
console.log(7);
//输出:1 3 4 7 5 2 6
首先,第一个 task 队列中的任务开始执行( 主线程 ),执行流遇到同步任务,打印出数字 1 。
console.log(1);
第二步,执行流遇到了 setTimeout ,setTimeout 为一个异步任务源,被分发到 task 队列中。
setTimeout(function() {
console.log(2);
}, 0)
第三步,执行流遇到了创建 Promise 实例,Promise 构造函数属于同步任务,因此会依次打印出数字 3 和 4 ,而后续的 then 方法里面的回调函数则会由主线程发布到 job 队列中去。
new Promise(function(resolve) {
console.log(3);
for (var i = 0; i < 1000; i++) {
if (i = 999) {
resolve();
}
}
console.log(4);
}).then(function() {
console.log(5);
})
第四步,执行流遇到了 click 事件,click 事件为一个异步任务源,同样被分发到 task 队列中。
document.querySelector("button").onclick = function() {
console.log(6);
}
第五步,另一个同步任务在主线程上开始执行,打印出数字 7 。
console.log(7);
第六步,此时,主线程中的同步任务执行完毕,Event Loop 开始循环 job 队列中所有可执行的回调函数,因此主线程发布的回调函数被拉到执行栈中开始执行,打印出数字 5 。
第七步,Event Loop 对 job 队列的第一轮循环结束,当然, Event Loop 会不间断地对 job 队列进行循环监听,而分发到 task 队列中的 setTimeout 在等待了给定时间之后,发布自己的任务(回调函数)到 job 队列中,Event Loop 会迅速捕捉到并拉到执行栈中执行,打印出数字 2 。
第八步,当点击事件发生时,task 队列中的 click 事件发布任务(回调函数)到 job 队列中,Event Loop 同样会迅速捕捉到并拉到执行栈中执行,打印出数字 6 。
示意图:
注意 task 队列和 job 队列的执行顺序:
以上内容只代表个人现阶段对于任务队列的理解。
热门评论
promise是微任务,setTimeOut是宏任务,异步队列里面 微任务的优先级>宏任务