Event Loop
JavaScript 在设计之初便是单线程,即指程序运行时,只有一个线程存在,同一时间只能做一件事
为了解决单线程运行阻塞问题, JavaScript 用到了计算机系统的一种运行机制,这种机制就叫做事件循环(Event Loop)
同步&异步任务
- 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
- 异步任务:异步执行的任务,比如
ajax网络请求,setTimeout定时函数等
同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就是事件循环

宏任务&微任务
异步任务还可以细分为微任务与宏任务
console.log(1)
setTimeout(()=>{
console.log(2)
}, 0)
new Promise((resolve, reject)=>{
console.log('new Promise')
resolve()
}).then(()=>{
console.log('then')
})
console.log(3)
单纯分同步&异步任务则会输出 -> 1 => 'new Promise' => 3 => 2 => 'then'- 但是实际上输出的是 -> 1=>'new Promise'=> 3 => 'then' => 2
原因在于异步任务执行顺序,事件队列其实是一个“先进先出”的数据结构,排在前面的事件会优先被主线程读取
例子中 setTimeout 回调事件是先进入队列中的,按理说应该先于 .then 中的执行,但是结果却偏偏相反
原因在于异步任务还可以细分为微任务与宏任务
宏任务
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
script整体代码setTimeoutsetIntervalsetImmediateI/OUI rendering
微任务
一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
Promise.thenMutationObserverprocess.nextTick(Node.js)Object.observe(废弃)
事件循环,宏任务,微任务的关系:
- 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
- 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完

async/await
async 是异步的意思,await 则可以理解为等待
async
async 函数是 Generator 函数的语法糖, async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数
async function fn() {
return 100;
}
fn().then(data => {
console.log(data); // 100
});
await
await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值
async function f(){
// 等同于
// return 123
return await 123
}
f().then(v => console.log(v)) // 123
不管 await 后面跟着的是什么,await 都会阻塞后面的代码
async function fn() {
console.log(1);
await fn2(); // 阻塞后面的代码
console.log(2);
}
async function fn2() {
console.log(3);
}
fn();
console.log(4);
上面代码中,await 会阻塞后面的代码(加入微任务队列),等到 fn2&&外面的 console.log(4) 执行完毕,才会回到 fn 执行 console.log(2)
结果为:1 3 4 2
promise 链式分析
new Promise((resolve) => {
resolve()
}).then(() => { // then1
console.log(1)
Promise.resolve()
.then(() => { // then2
console.log(2)
})
.then(() => { // then3
console.log(3)
Promise.resolve()
.then(() => { // then4
console.log(4)
}).then(() => { // then5
console.log(5)
})
}).then(() => { // then6
console.log(6)
})
}).then(() => { // then7
console.log(7)
}).then(() => { // then8
console.log(8)
Promise.resolve()
.then(() => { // then9
console.log(9)
}).then(() => { // then10
console.log(10)
})
}).then(() => { // then11
console.log(11)
})
执行过程
// 微任务队列 先进先出 入栈: ->[] 出栈: then ->
// 注意点:then中的同步代码执行完毕后 才会继续链式调用,将后面的then继续放入队列中
// -> [then1]
// then1 -> 执行同步代码 打印1 将then2放入队列 then1中的同步代码执行完毕 将then1后的then7放入队列
// -> [then7, then2]
// then2 -> 执行同步代码 打印2 then2中的同步代码执行完毕 将then2后的then3放入队列
// -> [then3, then7]
// then7 -> 执行同步代码 打印7 then7中的同步代码执行完毕 将then8放入队列
// -> [then8, then3]
// then3 -> 执行同步代码 打印3 定义了一个新的promise 将then4放入队列 then3中的同步代码执行完毕 将then3后面的then6放入队列中
// -> [then6, then4, then8]
// then8 -> 执行同步代码 打印8 定义了一个新的promise 将then9放入队列 then8中的同步代码执行完毕 将then8后面的then11放入队列中
// -> [then11, then9, then6, then4]
// then4 -> 执行同步代码 打印4 then4中的同步代码执行完毕 将then5放入队列
// -> [then5, then11, then9, then6]
// then6 -> 执行同步代码 打印6 then6中的同步代码执行完毕
// then9 -> 执行同步代码 打印9 then9中的同步代码执行完毕 将then10放入队列中
// -> [then10, then5, then11]
// -> then11 -> 执行同步代码 打印11 then11中的同步代码执行完毕
// -> then5 -> 执行同步代码 打印5 then5中的同步代码执行完毕
// -> then10 -> 执行同步代码 打印10 then10中的同步代码执行完毕