摘要:等到主任务队列执行完成时此时已打印,执行存在队列中的函数,任务队列中引入了任务队列来执行的回调函数。在这个的回调函数中使用创建一个的任务,同时在中调用函数创建一个任务。
本文讨论的事件循环均是基于浏览器环境上的,类似nodejs环境下的事件循环与此并不相同。
读者首先要对js单线程事件循环机制以及Promise有基本理解;如果这两个概念不是很清楚,建议先阅读下面两篇文章:
THE JAVASCRIPT EVENT LOOP ; Promise 对象
本文是基于THE JAVASCRIPT EVENT LOOP ,并对其内容的延伸,所以下面提到的概念都按这篇文章的来。首先我会总结一下 THE JAVASCRIPT EVENT LOOP 。OK,让我们开始吧。
1,消息队列(message queue)我们知道js单线程的实现方式会把异步任务(setTimeout回调函数,事件监听回调函数等)放在一个消息队列中;当主任务队列任务为空时会去message queue查询是否有等待执行的任务,如果有则执行。
例1:
var task_in_message_queue = () => {console.log("task in message queue")} setTimeout(task_in_message_queue,0); console.log("main task"); //result: //main task //task in message queue
setTimeout函数将task_in_message_queue函数添加到message queue队列中。等到主任务队列执行完成时(此时已打印main task),执行存在message queue队列中的task_in_message_queue函数
2,任务队列(job queue)ES6中引入了任务队列来执行Promise的回调函数。同message queue一样,job queue中的任务也是在主任务队列为空时才开始执行。
例2:
var promise = new Promise((resolve,reject) => { resolve("task in job queue"); }); var resolve_callback = (resolve_message) => {console.log(resolve_message)} promise.then(resolve_callback); console.log("main task"); //result: //main task //task in job queue /** 这里有一个有趣的现象 在chrome中打印出的结果是 main task task in job queue undefined //主任务的函数返回值 在firefox中的结果是 main task undefined //主任务的函数返回值 task in job queue 感觉v8的实现是把job queue整合到了主任务队列尾部 **/
promise.then 将promise fulfilled状态下的回调函数resolve_callback添加到job queue中。等到主任务队列执行完成时(此时已打印main task),执行存在job queue队列中的resolve_callback函数
这里有一点需要注意的是promise构造函数会在主任务中立即执行,例子如下:
var promise = new Promise((resolve,reject) => { resolve("task in job queue"); console.log("the promise construction executed"); }); var resolve_callback = (resolve_message) => {console.log(resolve_message)} promise.then(resolve_callback); console.log("main task"); //result: //the promise construction executed //main task //task in job queue3,任务队列(job queue)VS 消息队列(message queue)
通过上面的例子我们知道主任务队列优先级是最高的,那么job queue和message queue哪个优先级更高呢?答案是job queue,js会将job queue中的任务完全执行完之后再执行message queue中的任务。例子如下:
var message_task = () => {console.log("message task");} setTimeout(message_task,0); var promise1 = new Promise((resolve,reject) => { resolve("promise 1 resolved"); }); var promise2 = new Promise((resolve,reject) => { resolve("promise 2 resolved"); }); var resolve_callback = (resolve_message) => {console.log(resolve_message)} promise1.then(resolve_callback); promise2.then(resolve_callback); console.log("main task"); //result: //main task //promise 1 resolved //promise 2 resolved //message task /** 这里chrome和firefox返回undefined的位置同上面的例子一样,也是不同的。有兴趣的话可以试试看一下。 **/4,每次执行message queue中的任务前都会检查job queue吗?
现在我们知道job queue的优先级高于message queue。那么每次执行message queue中任务前会检查job queue吗?我的意思是如果当前job queue为空,message queue中有多个任务(假设有m_task1和m_task2)。js开始执行message queue中的任务,在执行完m_task1时插入了一个j_task1在job queue中。那么接下来是先执行m_task2呢还是j_task1呢?如果先执行了m_task2的话,就说明js一旦开始执行message queue中的任务就会将所有message queue中任务执行完再检查其它任务队列。如果先执行j_task1的话,那么说明再执行每个message queue中的任务前都会先检查其它任务队列,先执行优先级高的任务队列中的任务。为此我们用如下代码来检验:
var promise_task = new Promise((resolve,reject) => { resolve("j_task1"); }); var resolve_callback = (resolve_message) => {console.log(resolve_message)} var message_task1 = () => { promise_task.then(resolve_callback); console.log("m_task1"); } var message_task2 = () => {console.log("m_task2");} setTimeout(message_task1,0); setTimeout(message_task2,0); //result: //m_task1 //j_task1 //m_task2
事实证明js在每次执行message queue中的任务前都会检查其它任务队列(至少会检查job queue),根据队列优先级决定先执行哪个队列中的任务。
5,主任务队列呢?上面我们了解了job queue和message queue中任务的执行顺序,简而言之:在每次一个任务结束时,js都会根据任务队列的优先级判断下一个执行任务是哪个。如果job queue中有任务则执行job queue中的第一个任务,否则执行message queue中的第一个任务。那么主任务队列是不是也一样呢?(逻辑上应该是一样的,否则job queue或者message queue中的任务可以递归创建新任务,这样就永远无法回到主任务队列了)。
即每次选择执行任务前(或者每次任务结束后),js会根据主任务队列,job queue,message queue的优先级来挑选将要执行下一个任务是哪个。
为此我们声明一个promise和一个message_task函数。在这个promise的回调函数中使用setTimeout创建一个message_task的message queue任务,同时在message_task中调用promise.then 函数创建一个job queue 任务。这样两个任务会循环创建并循环执行。运行后我们会在console中看到两个任务循环打印,这是我们在console中键入alert("stop")命令。如果页面显示了alert,console停止了打印就说明主任务队列的行为方式和job queue,message queue是一样的。否则的话,在这种情况下我们将永远无法回到主任务队列。验证代码如下:
var promise_task = new Promise((resolve,reject) => { resolve("j_task"); }); var resolve_callback = (resolve_message) => { setTimeout(message_task,0); console.log(resolve_message); } var message_task = () => { promise_task.then(resolve_callback); console.log("m_task"); } promise_task.then(resolve_callback); //result: //console会循环打印 j_task 和 m_task //这是在console中键入alert("stop")命令,观察是否弹出alert框,console中打印是否终止
希望大家自行求证一下,当然验证完毕后记得刷新页面,不然可能就崩了。另:最好在chrome下验证,firefox有些卡顿。
总结js事件循环规律可大致总结为如下:
1,js中有三个任务队列:主任务队列,job queue,message queue;
2,它们的优先级是:主任务队列 > job queue > message queue;
3,每当要执行下一个任务前(或者一个任务完成后),js会根据优先级询问各个任务队列是否为空,一旦遇到非空任务队列时则取其第一个任务执行。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/99027.html
摘要:创建全局上下文由表示,并将全局上下文推到栈顶。在了解异步执行之前还需要知道一些概念,事件循环和回调队列也称为任务队列或消息队列。会等待事件循环调度。事件循环事件循环的作用是查看调用栈并确定调用栈是否空闲。 简评:如果你对 JavaScript 异步的原理感兴趣,这里有一篇不错的介绍。 JavaScript 同步代码是如果工作的 在介绍 JavaScript 异步执行之前先来了解一下, ...
摘要:常见应用则是为了完成一些更新应用程序状态的较小的任务,如处理的回调和的修改,以便让这些任务在浏览器重新渲染之前执行。常见应用执行顺序的实现需要至少一个和至少一个。 简介 我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue,其实在事件循环中 queue 一共有两种,还有一种叫 Job Queue 其中 Event Queue 在 HTML...
摘要:同时,如果执行的过程中发现其他函数,继续入栈然后执行。上面我们讨论的其实都是同步代码,代码在运行的时候只用调用栈解释就可以了。 序 Event Loop 这个概念相信大家或多或少都了解过,但是有一次被一个小伙伴问到它具体的原理的时候,感觉自己只知道个大概印象,于是计划着写一篇文章,用输出倒逼输入,让自己重新学习这个概念,同时也能帮助更多的人理解它~ 概念 JavaScript 是一门 ...
摘要:一旦这一切完成,方法会运行在类属性在命令构造后设置容器解析实例,在中我们设置了将使用的缓存驱动,我们也根据命令来决定我们调用什么方法。作业只在以上起效在上也无效处理作业方法调用触发事件触发事件。 译文GitHub https://github.com/yuansir/diving-laravel-zh 原文链接https://divinglaravel.com/queue-system...
摘要:包括了操作例如事件绑定,这类操作。每个结束后,都会进行也就是检查是否有在等待执行,根据先进先出,依次执行。简单来说,会检查是否有需要处理的,如果为空时,则会按照先进先出的顺序来处理中的。 众所周知, javascript是一个单线程语言。单线程也就意味着只有一个stack(调用栈),一次只能做一件事。那么又是如何实现异步操作?先来了解几个关键的术语。 Call Stack 调用栈 sh...
阅读 708·2021-09-28 09:35
阅读 2568·2019-08-29 11:25
阅读 2131·2019-08-23 18:36
阅读 1797·2019-08-23 16:31
阅读 2036·2019-08-23 14:50
阅读 3073·2019-08-23 13:55
阅读 3237·2019-08-23 12:49
阅读 2038·2019-08-23 11:46