摘要:包括了操作例如事件绑定,这类操作。每个结束后,都会进行也就是检查是否有在等待执行,根据先进先出,依次执行。简单来说,会检查是否有需要处理的,如果为空时,则会按照先进先出的顺序来处理中的。
众所周知, javascript是一个单线程语言。单线程也就意味着只有一个stack(调用栈),一次只能做一件事。那么又是如何实现异步操作?先来了解几个关键的术语。
Call Stack 调用栈
如图所示,在运行过程中,所有相关的变量是存于heap中,而call stack中是正在执行的代码。通过F12 developer tool 在debug模式也可以看到此时的call stack情况。下图是最简单的一种情况,函数根据调用顺序依次进入call stack,执行后再依次弹出(stack后进先出)。
那么JavaScript是怎么实现异步的呢?重要的task queue(任务队列)来了。
一般而言,我们所理解的异步操作,都是放入task queue进行等待。如下代码所示,console.log("start")进入call stack。而setTimeout是进入task queue进行等待。这里设置的时间为0,则是立即放入task queue,⚠️ 是放入task queue而不是立即执行。
只有在call stack为空的时候,event loop会讲task queue中的任务调入call stack再执行。
console.log("start"); setTimeout(()=>{ console.log("hey") } , 0) console.log("end");
输出结果:
start end heyMacrotask
一般而言,macrotask queue就是我们常说的task queue(也有人称为message queue)。Macrotask包括了setTimeout, Dom 操作(例如onLoad), click/mouse事件绑定,fetch response这类操作。实际上这些都是浏览器提供的API,所以在执行时是有它们多带带的线程去进行操作。举个例子,setTimeout()设置了2s的延迟,是浏览器设置了timer来计时,是另外的线程在等待2秒,js主线程不受影响,2s后回调函数再进入task queue。
(function() { console.log("this is the start"); setTimeout(function cb() { console.log("this is a msg from call back"); }); console.log("this is just a message"); setTimeout(function cb1() { console.log("this is a msg from call back1"); }, 0); console.log("this is the end"); })(); // "this is the start" // "this is just a message" // "this is the end" // undefined (注意此时是函数返回,因为没有设置返回值故输出undefined) // "this is a msg from call back" // "this is a msg from call back1"Microtask
ES6提供了Promise来进行异步操作。为了区别开task称为microtask。同上也有一个queue (job queue)来处理microtask。job queue拥有更高的优先级。每个task结束后,都会进行perform a microtask checkpoint.也就是检查job queue是否有microtask在等待执行,根据先进先出,依次执行。
console.log("script start"); setTimeout(function() { console.log("setTimeout"); }, 0); Promise.resolve().then(function() { console.log("promise1"); }).then(function() { console.log("promise2"); }); // script start // promise1 // promise2 // setTimeoutEvent loop
简单来说,event loop会检查queue是否有需要处理的task,如果call stack为空时,则会按照先进先出的顺序来处理queue中的task。而task分为microtask【Promise】和macrotask【setTimeout/DOM events/fetch】。优先处理microtask。一次event loop只会处理一次macrotask,并且是当microtask queue都处理结束后才会去处理macrotasks。
while (queue.waitForMessage()) { queue.processNextMessage(); }
强烈推荐大家去看 https://jakearchibald.com/201... 作者用动画的形式非常形象清晰地描述了过程。
参考文章
The JavaScript Event Loop
JavaScript Event Loop Explained
EventLoop | MDN
Tasks, microtasks, queues and schedules
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103290.html
摘要:事件循环了解了在引擎中是如何工作了之后,来看下如何使用异步回调函数来避免代码。从回调函数被放入后秒钟,把移到中。由于事件循环持续地监测调用栈是否已空,此时它一注意到调用栈空了,就调用并创建一个新的调用栈。 听多了JavaScript单线程,异步,V8,便会很想去知道JavaScript是如何利用单线程来实现所谓的异步的。我参考了一些文章,了解到一个很重要的词汇:事件循环(Event L...
摘要:主线程在任务队列中读取事件,这个过程是循环不断地,所以这种运行机制叫做事件循环是在执行栈同步代码结束之后,下一次任务队列执行之前。 单线程 javascript为什么是单线程语言,原因在于如果是多线程,当一个线程对DOM节点做添加内容操作的时候,另一个线程要删除这个DOM节点,这个时候,浏览器应该怎么选择,这就造成了混乱,为了解决这类问题,在一开始的时候,javascript就采用单线...
摘要:如果当前没有事件也没有定时器事件,则返回。相关资料关于的架构及设计思路的事件讨论了使用线程池异步运行代码。下一篇初窥事件机制的实现二中定时器的实现 在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力;在Node.js中,事件驱动模型则是其高并发能力的基础。 学习JavaScript也需要了解它的运行平台,为了更好的理解JavaScript的事...
摘要:浏览器与的异同,以及部分机制有人对部分迷惑,本身构造函数是同步的,是异步。浏览器的的已全部分析完成,过程中引用阮一峰博客,知乎,部分文章内容,侵删。 浏览器与NodeJS的EventLoop异同,以及部分机制 PS:有人对promise部分迷惑,Promise本身构造函数是同步的,.then是异步。---- 2018/7/6 22:35修改 javascript 是一门单线程的脚本...
阅读 1681·2023-04-25 23:43
阅读 872·2021-11-24 09:39
阅读 665·2021-11-22 15:25
阅读 1603·2021-11-22 12:08
阅读 1045·2021-11-18 10:07
阅读 2042·2021-09-23 11:22
阅读 3321·2021-09-22 15:23
阅读 2395·2021-09-13 10:32