Comments (67)
很棒的文章。纠正一点
我们都知道javaScript是单线程,渲染计算和脚本运行共用同一线程(网络请求会有其他线程),导致脚本运行会阻塞渲染。
渲染计算应该是浏览器GUI渲染线程负责,是由浏览器用c++编写的模块负责的。GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
from blog.
@webkonglong 文章涉及到规范,所以有些生涩,建议可以先看一些通熟易懂的介绍task和microtask的文章。
我在文中有一个实例,列出了每次循环task队列和microtask的变化,你所说的这个问题要弄清楚,首先得理解这轮event loop的task是哪个任务,其次setTimeout它只是一个task任务源,并不会立即执行,它只是将一个setTimeout任务插进task队列,得排到它,它里面的函数才会执行。Promise.then是microtask任务源,会将任务插进microtask队列。
Promise.resolve().then(function promise1 () {
console.log('promise1');
})
setTimeout(function setTimeout1 (){
console.log('setTimeout1')
}, 0)
以上面的例子来说,这两个api仅仅是将相应的任务插进他们各自的队列中,此次event loop执行的task并不是setTimeout里的任务,setTimeout的任务排在后边了,还没轮到它。microtask队列的任务是会在当轮清空的,所以会看到promise1先于setTimeout1执行。
from blog.
讲的太好了。。看过讲event loop最舒服的文章
尤其是用了大量的例子去验证浏览器渲染,一直想看这部分,今天终于如愿以偿
感谢作者
from blog.
@WinnieFE 当时我和你有一样的疑惑,写点我现在的理解!也欢迎一起来讨论
首先明确 Task -> MicroTask -> UI Render 顺序是一定的,Vue中对于异步更新的运用主要是维护异步队列dom更新合并,以及nextTick。
而nextTick的实质也是MicroTask,只是它会在执行时立刻追加到异步队列后面,而当你依次执行队列时,UI虽然没渲染,但是DOM其实已经更新了,注意:DOM更新是及时的,但是更新是异步的。
我相信你已经理解了!如果有异议也可以一起讨论下!
from blog.
我基本肯定第三个例子是错误的
在chrome中,setTimeout1和setTimeout2不是属于一个event loop;但在node中,是属于同一个event loop
因为node是执行完一个setTimeout队列(里面可能会有多个setTimeout callback),才会执行micro task;但chrome是执行完一个setTimeout callback,就会执行micro task
setTimeout(()=> {
console.log('setTimeout1')
new Promise((r) => {
r()
})
.then(() => {
console.log('timeout1-promise1')
})
}, 0)
setTimeout(()=> {
console.log('setTimeout2')
new Promise((r) => {
r()
})
.then(() => {
console.log('timeout2-promise1')
})
}, 0)
在node和chrome分别执行下这段js,应该就比较明朗了
from blog.
setTimeout(() => console.log('timeout')) Promise.resolve().then(() => { console.log('promise1') Promise.resolve().then(() => console.log('promise2')).then(() => console.log('aaa')) Promise.resolve().then(() => console.log('promise3')) }).then(() => console.log('promise4')) console.log('normal')没太理解,能不能麻烦详细解释一下执行流程,还有为什么这段新代码里面的
aaa
在最后阶段输出?
我来结合自己的理解讲一下:
// 1
setTimeout( // 1-1
() => { // 2
console.log('timeout') // 2-1
})
Promise.resolve() // 1-2
.then(() => { // 3
console.log('promise1') // 3-1
Promise.resolve() // 3-2
.then(() => { // 4
console.log('promise2') // 4-1
})
.then(() => { // 5
console.log('aaa') // 5-1
})
Promise.resolve() // 3-3
.then(() => { // 6
console.log('promise3') // 6-1
})
}).then(() => { // 7
console.log('promise4') // 7-1
})
console.log('normal') // 1-3
首先我为了区分每一个块,对你的代码进行了不会影响执行的格式调整,如果这个调整会导致与你执行的结果存在差异,请指出来。
我们再假设没有任何其他的js代码,将你的代码块放在一个script标签中,script里的代码被浏览器列为第一个task,放入task队列,并触发启动eventloop。
那么eventloop第一次循环的时候,执行栈为空,任务队列如下描述:
{task: [1], 其他队列:[] }
从task队列中找到最老的一个task,task队列中只有一个也就是script的task, 将这个task推入执行栈并执行这个task中的内容(也就是1这个代码块,1这个代码块中包含1-1,1-2,1-3三条同步代码;
我们看看这三条同步代码依次执行的详细过程(注意这三条语句肯定是在1那个task中执行完的)
- 1-1的同步语句是一个js的异步api,他将一个定时任务2代码块放入了 task,代码块1-1执行完毕
- 1-2是一个Promise.resolve()函数调用,并将promise1的状态改为fullfiled,then异步api将3代码放入 microtask
- 1-3是一个同步语句console,执行之,打印normal
这时候1task执行完毕,并出栈,执行栈再次为空并告诉eventloop,你可以继续给我代码执行了,eventloop就进入下一个循环点,接下来,我们先看看异步队列里面的任务
{task:[2], microtask: [3]}
(按照node的说法,eventloop会有多个循环执行点)eventloop进入到能够执行microtask的循环点,发现microtask队列中有3这个microtask,
将3这个microtask中的内容推入执行栈并执行3中的内容(包含3-1,3-2这俩条同步语句);(同样要注意3-1,3-2, 3-3也是在代码块3中执行完毕的)
- 3-1 console了promise1
- 3-2 Promise.resolve()函数调用,将 promise2的状态置为fullfilled,then异步api将4这个回调放入microtask异步队列
- 3-3 Promise.resolve()函数调用,将 promise3的状态置为fullfilled,then异步api将6这个回调放入microtask异步队列
这时候3这个microtask执行完毕,3代码块整个都出栈了, 然而promise1的状态为fullfilled,所以当3同步块执行完毕后,由于promise1的状态为fullfilled,7这个then异步api得以执行将7这个代码块放入microtask队列队尾
执行栈再次为空并告诉eventloop,你可以继续给我代码执行了,这时候eventloop不是到下一个循环点,
而是要继续检查microtask队列是否为空,为空就到下一个执行点,如果不是就继续执行microtask中的任务,直到microtask为空为止;那我们再看看各个队列的情况
{task:[2], microtask: [4, 6, 7]}
由于是队列(当然node中或者浏览器的实现中可能更复杂的数据结构,但是和队列相似),所以先进先出,那4先入队,4先执行
于是eventloop将4这个microtask推入执行栈并执行4中的内容(包含4-1)
- 4-1 只有一条语句块执行之,于是打印promise2;由于promise2的状态为fullfilled状态,于是then异步api得以执行,将5代码块放入microtask队列队尾中,这时候promise2还未结束,还有then回调在microtask中。
4 执行完毕
接着6同样的入栈执行
- 6-1 打印promise3, promise3执行完毕。
6执行完毕
接着7这个microtask要执行了
- 7-1 打印 promise4
接着5这个microtask要执行了
- 5-1 打印aaa
终于3这个then搞定了,
执行完后,eventloop会到一个循环结束点,示意eventloop循环结束。
我们再看看各个队列中
[task:[2],microtask:[]]
eventloop发现异步队列里面还有一个task,进行第二次循环,然后执行最老的一个task2, 然后打印timeout
from blog.
microtask 是个好东西
from blog.
疑问 or Bug
关于 文中 一句 script里的代码被列为一个task,放入task队列。
不知道这句是根据什么推测来的, 难道 script 也是一个 task 源?
说下个人的理解,不当之处请指正
HTML-Standard 中 8.1.3.4 一节 (calling-scripts)[https://html.spec.whatwg.org/#calling-scripts]
根据第九步 clean up after running script
当执行栈为空时, perform a microtask checkpoint,
我认为步骤应该是这样:
tick1:
1、 queue a microtask promise1
task queue [], microtask queue [promise1]
2、 queue a task setTimeout1
task queue [setTimeout1], microtask queue [promise1]
3、 queue a task setTimeout2
task queue [setTimeout1, setTimeout2], microtask queue [promise1]
此刻 execution context stack 为空 则
perform a microtask checkpoint
执行所有 microtask queue 里的microtask , 也就是 执行 microtask promise1, 执行完毕后 queue 是这样的
task queue [setTimeout1, setTimeout2], microtask queue []
tick2:
执行 task queue 中 oldest task, 也就是 setTimeout1, 同时 queue a microtask promise2, 执行完毕后
task queue [setTimeout2], microtask queue [promise2]
接着 perform a microtask checkpoint, 执行 microtask queue 中的所有microtask , 此时 也就是要执行 promise2 , 执行完毕后
task queue [setTimeout2], microtask queue []
tick3:
执行 task queue 中 oldest task, 也就是 setTimeout2
接着perform a microtask checkpoint 此时 microtask queue 为空。 执行完毕后
task queue [] microtask queue []
from blog.
十分感谢分享!有个疑问想请教下,应用一节提到 “可以看到如果task队列如果有大量的任务等待执行时,将dom的变动作为microtasks而不是task能更快的将变化呈现给用户。” 这里是将dom的变动操作放在microtasks中执行,那么如果要获得最新的DOM是在哪步执行呢? Vuejs的nextTick回调函数里说是可以获得最新DOM,可是UI rendering是在microtasks之后执行,那么是怎么获取到最新DOM的呢? 这点实在困惑
from blog.
@caiyongmin node里的event loop可以说和浏览器的event loop完全不是一个东西,node没有微宏任务的概念,规范只是约束了浏览器厂商。不要混淆nodejs和浏览器中的event loop这里有一篇文章可以看看,setTimeout1、setTimeout2是在timers阶段运行的,该阶段结束后才会执行promise2
from blog.
楼主,我刚试了一下第三个例子,好像是渲染了两次:
我用的chrome 67.0.3396.99
而且你的截图好像只截了setTimeout1,没有setTimeout2?
from blog.
楼主写的非常棒。有点小疑惑望不吝赐教,如果希望在每轮event loop都即时呈现变动,可以使用requestAnimationFrame
这句话的基础是每轮event loop都会检查执行requestAnimationFrame吧,并且有requestAnimationFrame回调会就一定会执行渲染。但实际应该是渲染前才会调用requestAnimationFrame吧,使用requestAnimationFrame并不能保证每轮都能渲染,只能保证渲染前一定调用了requestAnimationFrame的回调,求指教
from blog.
学习了 十分感谢
from blog.
@agileago 个人认为关键是then方法调用会创建一个新的promise,而这里的promise2和promise3已经标记为success了,aaa的新产生的promiseA要等promise2的第一个then结束后才会被标记为success,所以promise2和promise3会比aaa先执行
from blog.
用户代理可以理解为浏览器实现吗
from blog.
@hubu 我感觉可以这么理解
from blog.
感觉理解浏览器运行机制又深刻了一些,谢谢你的好文!
from blog.
运行microtask的条件是 上下文执行盏为空的时候,也就是在运行task之后,更新渲染之前。
settimeout属于task,promise属于microtask 照这样 settimeout 0秒要早promise执行了,我整体这么理解下来 有些懵,求解答 不知道可否加个微信或者qq,等你闲了 我请教你
from blog.
好文!想问一下浏览器为何要区分task和microtask呢?
from blog.
@zhanba 感觉选择eventloop这个异步模型,自然就得分出两种task才合理,一种是在当轮eventloop执行的,一种是往后某轮才执行的。如果没有microtask就没法在当轮eventloop里添加异步操作了,有点像人的左右手吧,少了一个就不完整了。
from blog.
请问将任务放到队列的操作是在哪里完成的?
from blog.
@jimczj 学习了,谢谢指正。
from blog.
@dreamdevil00 个人理解,<script>标签的解析和执行是在parse HTML这个阶段,是这个阶段的一部分,也就是最初构建DOM trees的时候。
在规范的8.1.4 Event loops中的8.1.4.1 Definitions里有这样的话:
An event loop has one or more task queues. A task queue is an ordered list of tasks, which are algorithms that are responsible for such work as:
Parsing
The HTML parser tokenizing one or more bytes, and then processing any resulting tokens, is typically a task.Callbacks
Calling a callback is often done by a dedicated task.
提到了 HTML parser是一个典型的task,当我们解析和执行完<script>标签里的内容,后续执行的回调函数才会分到指定的task中执行。
可以做个测试,在文档中间插入一个执行的脚本,使用Developer tools里的performance(原timeline)看到结果是parser HTML -> 执行脚本 -> parser HTML ..... , 执行脚本是会中断文档解析的,因为脚本可能会修改dom trees,所以最初的文档解析和脚本执行应该是一个连续的过程,所以<script>标签的代码也是一个task。
from blog.
@aooy 恩恩 有道理 这里有个问题想请教下 call stack 和 execution context stack 是一回事么
在网上看视频 Philip Roberts- Help I'm stuck in an event-loop.-Mobile
他是拿 call stack 来讲的 event loop
这个call stack 和 execution context stack 感觉有相似之处啊 是一回事么
from blog.
楼主看这个
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
from blog.
@fulvaz 我看过啊 这个我懂了。 我的问题是 call stack 和 execution context stack 的区别啊
from blog.
很棒的文章 我最近也在写这个原理 学习了很多之前比较模糊的点
from blog.
请教博主一个问题,烦请指教!当我反复查看html规范时候
An event loop has one or more task queues. A task queue is an ordered list of tasks, which are algorithms that are responsible for such work as:
- Events
Dispatching an Event object at a particular EventTarget object is often done by a dedicated task.
2.Parsing
The HTML parser tokenizing one or more bytes, and then processing any resulting tokens, is typically a task.
....
规范里面说到event loop有多个task queues.一个task queue就是任务的列表,它们负责以下工作。
看里很久有两个疑惑,恳请解答
1.dispatching an event 不是浏览器有专门的线程监听各种事件吗?然后相应的event handler 会进入task queues.为什么规范里说:是任务队列Dispatching an Event。因为任务队列里的任务都会被JavaScript engines执行,也就是说是JavaScript engines在触发事件?
2.html解析不是通过渲染引擎来解析的吗,怎么这里又说是JavaScript engines解析?在一次event loop中,如果页面需要更新。这时候会停止JavaScript引擎,启动渲染引擎来更新页面吗?
谢谢了
@aooy
from blog.
Promise.resolve().then(function promise1 () {
console.log('promise1');
})
setTimeout(function setTimeout1 (){
console.log('setTimeout1')
Promise.resolve().then(function promise2 () {
console.log('promise2');
})
}, 0)
setTimeout(function setTimeout2 (){
console.log('setTimeout2')
}, 0)
这个例子在 node.js 是运行的不一样的结果
promise1
setTimeout1
setTimeout2
promise2
➜ ~ node -v
v6.11.3
from blog.
�这个翻译的是W3c的标准吧,不是whatwg的标准,我最近也从规范入手理解Event loop呢
from blog.
@WinnieFE 在Vue中更新一个注册到data中的属性,此时引用该值的地方,比如temlate中是不会立即执行的,所以直接获取对应的DOM节点得到的也还是原来的值,这是因为在更新该属性之后,Vue需要依次去执行注册到该属性上的监听函数去更新对应的应用,只有这一系列监听函数执行完成之后,DOM才是最新的,而我们手动注册的$nextTick方法会在这之后执行,所以可以获得更新后的DOM, 以上是个人理解,如果有误,请指正一下
from blog.
楼主,我刚试了一下第三个例子,好像是渲染了两次:
我用的chrome 67.0.3396.99
而且你的截图好像只截了setTimeout1,没有setTimeout2?
是的,我测试的时候也是两次
from blog.
setTimeout(() => console.log('timeout'))
Promise.resolve().then(() => {
console.log('promise1')
Promise.resolve().then(() => console.log('promise2'))
Promise.resolve().then(() => console.log('promise3'))
}).then(() => console.log('promise4'))
console.log('normal')
楼主这个代码的运行结果为什么会是 1,2, 3,4
而不是1,4,2,3
,难道在执行microTask
时产生的microTask
优先级更高?
from blog.
setTimeout(() => console.log('timeout')) Promise.resolve().then(() => { console.log('promise1') Promise.resolve().then(() => console.log('promise2')) Promise.resolve().then(() => console.log('promise3')) }).then(() => console.log('promise4')) console.log('normal')楼主这个代码的运行结果为什么会是
1,2, 3,4
而不是1,4,2,3
,难道在执行microTask
时产生的microTask
优先级更高?
不是因为优先级高,而是因为执行2、3的时候,4还没加到microTask中
from blog.
setTimeout(() => console.log('timeout'))
Promise.resolve().then(() => {
console.log('promise1')
Promise.resolve().then(() => console.log('promise2')).then(() => console.log('aaa'))
Promise.resolve().then(() => console.log('promise3'))
}).then(() => console.log('promise4'))
console.log('normal')
@lmule 没太理解,能不能麻烦详细解释一下执行流程,还有为什么这段新代码里面的aaa
在最后阶段输出?
from blog.
@Yatoo2018 理解了,谢谢分析
from blog.
既然异步的任务分两种,一种task,另一种microtask,那同步任务又叫啥task呢?而且不管同步还是异步的task都会进入到调用栈,那同步的task是从哪进入调用栈的呢?小白求解释~~~
from blog.
当用户代理安排一个任务,必须将该任务增加到相应的event loop的一个
tsak=> [task] 队列中。
文中有个小错误
from blog.
con.textContent = 0;
例子2里面小错误,不是textContext
from blog.
博主您好,我有一个疑问。当JS访问某些属性的时候会触发“立即回流/重绘”,比如offsetWidth等。
那么我想请问一下,当我的代码中访问offsetWidth时,会触发“立即回流/重绘”时,但是这段代码还没有执行完毕,那这不就相当于我当前的task还没有执行完毕、就去执行UI rendering了吗?
var node = document.getElementById('id')
node.offsetWidth // => 浏览器在这里中断,进行回流重绘,然后再返回来继续运行后续代码?
// 后续代码
或者是我对回流/重绘理解有误吗?回流/重绘≠UI rendering?
谢谢您解答!
from blog.
‘’一个event loop有一个或者多个task队列‘’ 这句话不是很理解,一个loop不是只包含一个task队列和一个微任务队列吗
from blog.
@Tvinsh 浏览器确实是这样的,只有一个宏队列。但是在NodeJS中,不同的macrotask对应着不同的宏队列:
- Timers Queue:setTimeout() 和 setInterval() 的回调
- IO Callbacks Queue:用户输入的回调,如键盘、鼠标事件
- Check Queue:setImmediate的回调
- Close Callbacks Queue:一些准备关闭的回调函数,如:socket.on('close', ...)
取出下一个宏任务的时候,会从上一个宏任务所在队列开始往后检查是否有下一个任务。也就是说,如果当前宏队列还有任务,那么取出一个执行;如果当前宏队列没有任务,会执行下一个宏队列的任务(而不是回到第一个Timers Queue中)。
同样,浏览器中只有一个微队列,而node中有两个:
- Next Tick Queue:是放置process.nextTick(callback)的回调任务的
- Other Micro Queue:放置其他microtask,比如Promise等
两个微队列的任务也是按队列顺序依次执行。
要注意NodeJS与浏览器的执行顺序的不同,NodeJS的执行阶段是这样的:
- 先执行全局Script代码
- 执行完同步代码调用栈清空后,先从微任务队列Next Tick Queue中依次取出所有的任务放入调用栈中执行,再从微任务队列Other Microtask Queue中依次取出所有的任务放入调用栈中执行
- 然后开始宏任务的6个阶段,每个阶段都将该宏任务队列中的所有任务都取出来执行(注意,这里和浏览器不一样,浏览器只取一个)
- 6个阶段执行完毕后,再开始执行微任务,以此构成事件循环
from blog.
每个线程都有自己的event loop。
那么event loop是线程中的循环结构吗?感谢
@aooy
from blog.
楼主,我刚试了一下第三个例子,好像是渲染了两次:
我用的chrome 67.0.3396.99
而且你的截图好像只截了setTimeout1,没有setTimeout2?
我试的也是一次
from blog.
楼主,我刚试了一下第三个例子,好像是渲染了两次:
我用的chrome 67.0.3396.99
而且你的截图好像只截了setTimeout1,没有setTimeout2?我试的也是一次
理论上应该是两次啊
from blog.
楼主,我刚试了一下第三个例子,好像是渲染了两次:
我用的chrome 67.0.3396.99
而且你的截图好像只截了setTimeout1,没有setTimeout2?是的,我测试的时候也是两次
wtf 你引用的是我去年发的,我刚试了下最新的chrome 80.0.3987.116,又变成了一次,难道楼主未卜先知。。。
from blog.
取出下一个宏任务的时候,会从上一个宏任务所在队列开始往后检查是否有下一个任务。也就是说,如果当前宏队列还有任务,那么取出一个执行;如果当前宏队列没有任务,会执行下一个宏队列的任务(而不是回到第一个Timers Queue中)。
@imageslr您好,执行完一个宏任务之后不应该是执行该宏任务后面的微任务么?你看下面就是promise在second timeout之前,不知道有没有正确理解你的意思,请指教
`setTimeout(
() => {
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(()=>{
console.log('promise')
});
process.nextTick(()=>{
console.log('tick');
})
setImmediate(() => {
console.log('immediate');
});
},0)
setTimeout(()=>{
console.log('second timeout')
})`
输出:
tick
promise
second timeout
immediate
timeout
from blog.
postMessage是task,那它是在,setTimeout1之后插入的,为啥会在 setTimeout1 之前执行呢?
setTimeout(function setTimeout1(){
console.log('setTimeout1')
}, 0)
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1 (){
console.log('postMessage')
Promise.resolve().then(function promise1 (){
console.log('promise1')
})
};
channel.port2.postMessage(0);
setTimeout(function setTimeout2(){
console.log('setTimeout2')
}, 0)
console.log('sync')
from blog.
所有同源的browsing contexts可以共用event loop,这样它们之间就可以相互通信。
这句话是有前提的吧,否则效果太吓人了。这句话出自哪里?
from blog.
在一轮event loop中多次修改同一dom,只有最后一次会进行绘制。
楼主,小结第一条是不是描述有误,应该是可能会绘制,并且只绘制一次。
如果想每一次event loop都进行绘制,那么就要在渲染之前使用requestAnimationFrame对DOM进行修改。
from blog.
指正 task queue 不是队列,而是 set
规范:
Task queues are sets, not queues, because step one of the event loop processing model grabs the first runnable task from the chosen queue, instead of dequeuing the first task
from blog.
关于第一题,我发现 只修改 textContent
或者 innerHTML
的话貌似不会触发 Layout 和 Paint,只会触发 Schedule Style Recalculation。
在 append 新的元素后,会触发 Layout 和 Paint。
Chrome 版本 83.0.4103.97(正式版本) (64 位)
<div id='con'>this is con</div>
<script>
var t = 0;
var con = document.getElementById('con');
con.onclick = function () {
setTimeout(function setTimeout1() {
// 只有这行只会触发 Schedule Style Recalculation
con.textContent = t;
// 加上下面的代码后,会触发 Layout 和 Paint
const child = document.createElement('div');
child.innerHTML = 'I am child';
con.appendChild(child);
}, 0)
};
</script>
相关截图
from blog.
postMessage是task,那它是在,setTimeout1之后插入的,为啥会在 setTimeout1 之前执行呢?
setTimeout(function setTimeout1(){ console.log('setTimeout1') }, 0) var channel = new MessageChannel(); channel.port1.onmessage = function onmessage1 (){ console.log('postMessage') Promise.resolve().then(function promise1 (){ console.log('promise1') }) }; channel.port2.postMessage(0); setTimeout(function setTimeout2(){ console.log('setTimeout2') }, 0) console.log('sync')
估计浏览器实现不一样吧。。测了下safari(13.0.2),输出和想法一致。。当然,firefox在非特殊场景中也是一样。具体(https://stackoverflow.com/questions/62421016/event-execution-sequence-and-rendering)
from blog.
好不容易搞清楚了宏任务和微任务,现在又被ui渲染困住了,请问如果宏任务或微任务执行超过16.7ms,那是会等结束后立即执行渲染吗?还有requestAnimationFrame是在一轮循环内执行,还是一定在每次渲染前执行。加上ui渲染后好迷惑啊
from blog.
@97Yates05 并不是每一轮的event loop都伴随着渲染,浏览器会基于一些“规则”,比如这次渲染不会带来视觉上的改变会跳过这次渲染,详情见(https://html.spec.whatwg.org/multipage/webappapis.html#event-loops),而RAF是在渲染前执行的,当然在不同的chrome版本测试可能是有问题的,比如version 80,存在这个bug。
from blog.
相逢恨晚,文章写得非常好,感谢作者
from blog.
好文!想问一下浏览器为何要区分task和microtask呢?
像setTimeout这种task是浏览器本身提供的(没有ecma层面的规范);promise这种microtask是ecma语言层面的规范,由浏览器来实现。
所以可以说promise出现之前,js的异步靠浏览器提供的api,promise出现后js有了语言层面的异步操作
from blog.
同感同感,这是我看得最舒服也学到最多的文章了
from blog.
from blog.
postMessage是task,那它是在,setTimeout1之后插入的,为啥会在 setTimeout1 之前执行呢?
setTimeout(function setTimeout1(){ console.log('setTimeout1') }, 0) var channel = new MessageChannel(); channel.port1.onmessage = function onmessage1 (){ console.log('postMessage') Promise.resolve().then(function promise1 (){ console.log('promise1') }) }; channel.port2.postMessage(0); setTimeout(function setTimeout2(){ console.log('setTimeout2') }, 0) console.log('sync')
因为,settimeout 默认有 1ms 延时。虽然先进入 task queue。
但是任务执行去取 task 的时候,由于这个延时,会先取出后面的 postmessage task
from blog.
我这里有一段代码,可以麻烦您讲解一下执行流程吗,多谢多谢?
`async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
return new Promise(function (resolve, reject) {
console.log('promise2')
reject('promise2 reject');
}).then(function (res) {
console.log(res)
}).catch(err => {
console.log(err)
});
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1')
resolve();
}).then(function () {
console.log('promise1 resolve')
})
console.log('script end')`
from blog.
from blog.
@WinnieFE 当时我和你有一样的疑惑,写点我现在的理解!也欢迎一起来讨论
首先明确 Task -> MicroTask -> UI Render 顺序是一定的,Vue中对于异步更新的运用主要是维护异步队列dom更新合并,以及nextTick。
而nextTick的实质也是MicroTask,只是它会在执行时立刻追加到异步队列后面,而当你依次执行队列时,UI虽然没渲染,但是DOM其实已经更新了,注意:DOM更新是及时的,但是更新是异步的。
我相信你已经理解了!如果有异议也可以一起讨论下!
其实就是vue的nextTick可以拿到还没有绘制出来的dom,拿到的dom除了还没绘制之外已经可以进行任何api操作了
from blog.
楼主写的非常棒。有点小疑惑望不吝赐教,
如果希望在每轮event loop都即时呈现变动,可以使用requestAnimationFrame
这句话的基础是每轮event loop都会检查执行requestAnimationFrame吧,并且有requestAnimationFrame回调会就一定会执行渲染。但实际应该是渲染前才会调用requestAnimationFrame吧,使用requestAnimationFrame并不能保证每轮都能渲染,只能保证渲染前一定调用了requestAnimationFrame的回调,求指教
个人认为是这样的
from blog.
好文!想问一下浏览器为何要区分task和microtask呢?
比如你去一个饭店吃饭,如果只能到店排队的话那么你只能到店后从最后开始排队,但是如果提供可以取号排队,你可以在需要去饭店的前半个小时就开始排队,等你到店就差不多吃上饭了~上面是一个比较形象的例子,但是也可以看出task的局限性就是无法处理高优先级的任务,microtask 提供了一种方式可以处理高优先级的任务。作者文章中也做了比较好的解释。
from blog.
worker也是一种用户代理,我理解能够提供一个完整的上下文环境,有着自己的事件循环处理模型的一个终端,都可以称之为一种用户代理
from blog.
作者你好,关于第九个例子,我在运行时的结果和示例不一样
如下:
sync
setTimeout1
postMessage
promise1
setTimeout2
即setTimeout并不会因为4ms的规定而晚于postMessage执行
调换postMessage与setTimeout1的顺序
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage')
Promise.resolve().then(function promise1() {
console.log('promise1')
})
};
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0)
console.log('sync')
打印如下:
sync
setTimeout1
postMessage
promise1
setTimeout2
表明setTimeout1会早于postMessage执行
但如果为setTimeout指定1ms的延迟会发现它晚于postMessage执行,这种情况可能是什么原因导致的
上述结果的浏览器环境
mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/120.0.0.0 safari/537.36 edg/120.0.0.0
在更早版本的Chrome浏览器中,会得到和示例一样的结果,好像和浏览器有关
旧版本浏览器
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
setTimeout(function setTimeout1() {
console.log('setTimeout0')
}, 0)
console.log("1st log")
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage')
Promise.resolve().then(function promise1() {
console.log('promise1')
})
};
console.log("2nd log")
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0)
1st log
2nd log
postMessage
promise1
setTimeout0
setTimeout1
setTimeout2
from blog.
Related Issues (7)
- aoy的说明书 HOT 6
- 解析vue2.0的diff算法 HOT 53
- 滑动效果的原理及实践一个滑动小插件 HOT 7
- 6种图片灰度转换算法 HOT 2
- 关于event loop的task 和microtask的问题 求解惑 HOT 1
- 求个博主联系方式
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blog.