我们都知道,Javascript
是单线程
语言,也就是说,同一个时间只能做一件事。这就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
因为任务有同步
与异步
之分,所以不同任务的执行必定有一定的顺序。
Event Loop(事件循环)
- 所有任务可以分成两种:
同步任务
(synchronous):在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务异步任务
(asynchronous):不进入主线程、而进入任务队列
(task queue)的任务,只有任务队列
通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
- 异步执行的运行机制:(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
- 所有
同步任务
都在主线程
上执行,形成一个执行栈
(execution context stack)。 - 主线程之外,还存在一个
任务队列
(task queue)。只要异步任务有了运行结果,就在任务队列
之中放置一个事件。 - 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取
任务队列
,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 - 主线程不断重复上面的第三步。
定义: 主线程
循环不断的从任务队列
中读取事件(任务
),这整个过程就称为Event Loop
(事件循环
)。
JavaScript事件
概述: 任务队列
是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在任务队列
中添加一个事件,表示相关的异步任务可以进入执行栈
了。主线程读取任务队列
,就是读取里面有哪些事件。
JavaScript的事件分两种,宏任务
(macro-task)和微任务
(micro-task)
宏任务
:从上到下、从左到右、顺序执行的script代码,setTimeout,setInterval,setImmediate(node环境)微任务
:Promise.then(非new Promise),async/await整体,process.nextTick(node环境)- 其他异步任务:
requestAnimationFrame(callback)
- RAF 仅仅存在于浏览器环境,执行时机在setTimeout
之前执行queueMicrotask
:执行时机在第一轮同步任务
执行完成之后,微任务
执行之前。
setTimeOut
setTimeOut
执行需要满足两个条件:
- 主进程必须是空闲的状态,如果到时间了,主进程不空闲也不会执行你的回调函数
- 这个回调函数需要等到插入异步队列时前面的异步函数都执行完了,才会执行
注意: setTimeOut
并不是直接的把回调函数放进异步队列中,而是在定时器的时间到了之后,把回调函数放到执行异步队列中去。如果此时这个队列已经有很多任务了,那就排在他们的后面。这也就解释了为什么setTimeOut为什么不能精准的执行的问题了。
promise、async/await
new Promise
是同步
的任务,会被放到主进程
中去立即执行。.then()
函数是异步任务会放到异步队列
中去,具体就是当promise状态结束(fulfilled
)的时候,会立即放进异步队列
中去。- 带
async
关键字的函数会返回一个promise
对象,如果里面没有await
,执行起来等同于普通函数
。 await
关键字要在async
关键字函数的内部
,await
写在外面会报错。await
如同他的语意,就是在等待,等待右侧的表达式完成。await
会让出线程,阻塞async
内后续的代码,先去执行async
外的代码。等外面的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise
对象,是一个同步函数
,也会同样执行。
执行顺序(总结):
- 宏任务(从上到下、从左到右的整体)
- 微任务的Event Queue(Promise.then,async / await整体,process.nextTick【node环境】)
- 宏任务的Event Queue(setTimeout / setInterval / setImmediate【node环境】)
- 同一轮
微任务队列
中,依次顺序执行process.nextTick
、queueMicrotask
、Promise.then
和async/await
- 同一轮
宏任务队列
中,setImmediate
在setTimeout
之后执行- 在
浏览器环境
同一轮任务队列中,requestAnimationFrame
在setTimeout
之前执行
经典面试题:
1、setTimeout + promise
- 例1:
1 | ;(function() { |
- 输出:
1 | 1 |
- 例2:
1 | function asyncFn() { |
- 输出:
1 | 1 |
2、setTimeout + promise + async + await
- 例3:
1 | ;(function() { |
- 输出:
1 | script start |
3、setTimeout + promise + async + await + setImmediate + process.nextTick
- 例4(在
node
环境中):
1 | console.log('global'); |
- 输出:
1 | global |
总结
- script
- process.nextTick (node环境)
- queueMicrotask
- promise.then / async|await
- requestAnimationFrame (浏览器环境)
- setTimeout
- setImmediate (node环境)
参考文档:
欢迎访问:天问博客
本文作者: Tiven
发布时间: 2021-10-09
最后更新: 2023-03-02
本文标题: JS中setTimeout、promise、async、await的执行顺序
本文链接: https://www.tiven.cn/p/4d100461/
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可。转载请注明出处!
发布时间: 2021-10-09
最后更新: 2023-03-02
本文标题: JS中setTimeout、promise、async、await的执行顺序
本文链接: https://www.tiven.cn/p/4d100461/
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可。转载请注明出处!