引言
- js 引擎是单线程, 所以同一个时间只能做一件事.
- js 的执行顺序核心是队列 , 而执行的核心是执行栈. 总是从事件队列取事件执行.
- js 代码有同步执行代码和异步代码, 却并不是以一段代码要执行的时间做区分.
- 解析到
<script>
标签 , 或者成功加载一个脚本文件, 是在事件队列中添加了一个事件.
粗略流程模拟
浏览器开启 -> js 引擎开启 ->
开启定时扫描宏任务队列( task queue ) -> 任务队列有事件了 -> 暂停定时扫描 -> 取出任务到执行栈执行 -> 预编译 -> 执行 -> [ 同步执行, 异步代码抛出去 ] -> 扫描微任务队列( jobs )-> 将任务踢出执行栈 -> 栈空 ->
开启定时扫描宏任务队列( task queue ) ….
js 代码本身不负责异步代码执行, js 负责找到对应事件, 然后交给浏览器对应的处理线程处理 , 然后继续执行.
异步有哪些?
- task
- script 标签解析 , 脚本文件加载
- 定时类 setTimeout , setInterval
- http 请求 : ajax 请求 + 文件加载 onload, onerror
- jobs
- promise
示例
1. 仅 task
console.log('start');
setTimeout(()=>{ console.log('timeout') },0);
console.log('end');
// 打印结果: start end timeout
一段
<script>
脚本 , 就是一个任务(task
) ,task
里面不执行执行其他的task
, 只执行 本身task
和jobs
;
- 第一个
task
=><script>
<script>
task 执行结果: 打印 start end ,setTimeout
丢给 “定时处理线程”jobs
中没有任务 , 跳过- 第一个
task
结束 - 扫描
task queue
- 下一个 task =>
setTimeout task
setTimeout task
执行结果 : 打印 timeoutjobs
中没有任务 , 跳过- 第二个
task
结束 - 扫描
task queue
2. task 和 jobs 并存
console.log('start');
new Promise((resolve,rejct)=>{
console.log('promise start');
resolve()
})
.then(()=>{
console.log('promise then1')
})
.then(()=>{
console.log('promise then2')
})
setTimeout(()=>{ console.log('timeout') },0)
console.log('end');
// 打印结果:start , promise start , end, promise then1 ,promise then2, timeout
new Promise 传递的函数 , 是立即执行的 , 而 setTimeout 传递的函数是回调函数.
- 第一个
task
=><script>
<script>
同步执行① 打印 start
- new Promise(fn).then() , 其中 fn 和 then 都为同步任务
- fn 执行,
② 打印 promise start
- resolve 执行, 将 Promise fulfilled 状态要执行的方法 ,推入
jobs queue
- then 执行, 将 then 的参数( 函数 )加入 promise 的 fulfilled 状态要执行的方法的列表.
setTimeout
丢给 “定时处理线程”- console.log(‘end’)
③ 打印 end
- 执行栈空, 扫描
jobs queue
- 取出
jobs queue
中 Promise fulfilled 方法列表 => 两个 then 传入的函数依次执行 - 第一个 then ,
④ 打印 promise then1
; 第二个 then⑤ 打印 promise then2
- 执行栈空 , 扫描
jobs queue
,jobs queue
空 - 一次循环完成
- 继续扫描
task queue
, 找到 setTimeout 回调事件 - setTimeout 回调执行,
⑥ 打印 timeout
- 执行栈空, jobs queue 空 , task queue 空.
注意: 其中 new Promise 同步执行的不只是 Promise 传入的函数, 还有后面的两个 then, then 并不是扫描
jobs queue
后才执行,jobs queue
执行的是then
里面的函数.
可以改写一下 promise 的结构证明:
console.log('start')
new Promise((resolve, rejct) => {
console.log('promise start');
resolve()
})
.then((function () {
console.log('then is runing')
return function () {
console.log('promise then1')
}
})())
.then(() => {
console.log('promise then2')
})
setTimeout(() => { console.log('timeout') }, 0)
可以看出
then is runing
在end
之前.
总结
- 在部分浏览器中, 并未把 promise 当成一个 jobs , 而是直接列为一个新的 task .
- js 执行, 是一系列的 task 的有序执行 , 一个 task 又可以生成新的 task 和 新的 jobs, 一个jobs 又可以生成新的 task 和 jobs , 一个 task 的执行完成包括其所有 jobs 的完成.
相关链接
- [javascript 进阶之 - promise]