资讯专栏INFORMATION COLUMN

js 事件循环中的job queue和message queue

songze / 3153人阅读

摘要:等到主任务队列执行完成时此时已打印,执行存在队列中的函数,任务队列中引入了任务队列来执行的回调函数。在这个的回调函数中使用创建一个的任务,同时在中调用函数创建一个任务。

本文讨论的事件循环均是基于浏览器环境上的,类似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 queue
3,任务队列(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 同步代码是如果工作的 在介绍 JavaScript 异步执行之前先来了解一下, ...

    tolerious 评论0 收藏0
  • 浅析 JS 事件循环之 Microtask Macrotask

    摘要:常见应用则是为了完成一些更新应用程序状态的较小的任务,如处理的回调和的修改,以便让这些任务在浏览器重新渲染之前执行。常见应用执行顺序的实现需要至少一个和至少一个。 简介 我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue,其实在事件循环中 queue 一共有两种,还有一种叫 Job Queue 其中 Event Queue 在 HTML...

    sihai 评论0 收藏0
  • 浅析 JS 中的 EventLoop 事件循环(新手向)

    摘要:同时,如果执行的过程中发现其他函数,继续入栈然后执行。上面我们讨论的其实都是同步代码,代码在运行的时候只用调用栈解释就可以了。 序 Event Loop 这个概念相信大家或多或少都了解过,但是有一次被一个小伙伴问到它具体的原理的时候,感觉自己只知道个大概印象,于是计划着写一篇文章,用输出倒逼输入,让自己重新学习这个概念,同时也能帮助更多的人理解它~ 概念 JavaScript 是一门 ...

    chadLi 评论0 收藏0
  • 剖析Laravel队列系统--Worker

    摘要:一旦这一切完成,方法会运行在类属性在命令构造后设置容器解析实例,在中我们设置了将使用的缓存驱动,我们也根据命令来决定我们调用什么方法。作业只在以上起效在上也无效处理作业方法调用触发事件触发事件。 译文GitHub https://github.com/yuansir/diving-laravel-zh 原文链接https://divinglaravel.com/queue-system...

    CollinPeng 评论0 收藏0
  • Javascript event loop

    摘要:包括了操作例如事件绑定,这类操作。每个结束后,都会进行也就是检查是否有在等待执行,根据先进先出,依次执行。简单来说,会检查是否有需要处理的,如果为空时,则会按照先进先出的顺序来处理中的。 众所周知, javascript是一个单线程语言。单线程也就意味着只有一个stack(调用栈),一次只能做一件事。那么又是如何实现异步操作?先来了解几个关键的术语。 Call Stack 调用栈 sh...

    reclay 评论0 收藏0

发表评论

0条评论

songze

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<