[toc]
# 异步编程
## 哪些代码是异步代码?
异步代码有两类:`微任务代码` 和 `宏任务代码`。
- 微任务(microtask)
process.nextTick(node独有),
Promises.then,
MutationObserver
- 宏任务(macrotask):
AJAX、
setTimeout,
setInterval,
setImmediate(node独有)、
事件回调(鼠标键盘事件等)、
I/O
## 代码的执行顺序
同步 -> 异步 ( 微任务 -> 宏任务)
示例代码、
```
// 同步 1
console.log('a')
// 宏任务 5
setTimeout(() => {
console.log('b')
})
// 同步 2
console.log('c')
// 微任务 4
Promise.resolve().then(() => console.log('r'))
// 同步 3
console.log('e')
// 最终的输出结果:acerb
```
## 如何将异步代码封装成 Promise 对象?
代码演示:
```
// 有一段异步代码
setTimeout(()=>{
console.log('hello')
})
// 把上面的异步代码封装成 Promise 对象
// 1. new Promise 对象
const p = new Promise(function(resolve, reject){
// 2. 把异步代码放到里面
setTimeout(()=>{
console.log('hello')
// 3. 在异步代码执行结束之后改变 Promise 的状态
if (成功) {
// 如果成功就调用 resolve 方法设置为 fulfilled 状态
resolve(成功返回的数据)
} else {
// 如果失败就调用 reject 方法设置为 rejected 状态
reject(返回的错误信息)
}
})
})
```
## Promise 是干什么用的?为什么要用?
异步代码需要使用回调函数,当需要按顺调用多个异步代码时,就必须一层一层的嵌套回调函数,这样的代码比较麻烦比较不好维护,这种多层嵌套的回调函数我们称之为 `回调地狱`:
~~~
// 连续执行4段异步代码,需要一层一层“向内套”
setTimeout(()=>{
console.log(1)
setTimeout(()=>{
console.log(2)
setTimeout(()=>{
console.log(3)
setTimeout(()=>{
console.log(4)
})
})
})
})
~~~
为了避免编写回调地狱的代码,ES6 中提供了 Promise 来解决这个问题:把异步代码封装成 Promise 对象之后,代码会变成 `扁平` 而不是 `层层向里套`:
~~~
// 1、把异步代码封装成 Promise 对象:
let p1 = function() {
return new Promise((resolve,reject)=>{
// 异步代码
setTImeout(()=>{
console.log(1)
resolve()
})
})
}
let p2 = function() {
return new Promise((resolve,reject)=>{
// 异步代码
setTImeout(()=>{
console.log(2)
resolve()
})
})
}
let p3 = function() {
return new Promise((resolve,reject)=>{
// 异步代码
setTImeout(()=>{
console.log(3)
resolve()
})
})
}
let p4 = function() {
return new Promise((resolve,reject)=>{
// 异步代码
setTImeout(()=>{
console.log(4)
resolve()
})
})
}
// 2. 依次调用(避免回调地狱)
p1().then(()=>{
return p2()
}).then(()=>{
return p3()
}).then(()={
return p4()
})
~~~
## async ... await ... 是干什么用的?如何用?
Promise 虽然可以解决回调地狱的问题,但是还需要使用 .then 、.catch ,还是比较麻烦,所以为了进一步优化异步代码,ES8 在 Promise 的基础上又添加了 async/await 语法,可以 `以同步的方式编写异步代码`。
示例代码、
~~~
// 上面封装好的 Promise 对象,可以直接以下面的方式调用,
// 下面的函数 p1 执行完之后 ,才执行 p2,然后p3,然后p4
// await 必须要用在 async function 中
// await 的返回值就是异步代码成功时的返回值
// async 的返回值又是一个新的 Promise 对象
const p = async function(){
await p1()
await p2()
await p3()
await p4()
}
// p 还是一个 Promise 对象
p().then(()=>{console.log('完成'})
~~~