为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript异步编程之任务队列

单线程

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 队列:eventajax(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 队列的执行顺序:
图片描述


以上内容只代表个人现阶段对于任务队列的理解。

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消