持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
我们先了解一下回调地狱是什么,什么是异步代码和Promise的使用流程和原理
回调地狱是什么:
js的异步代码一层层的回调(异步回调,层层嵌套)
例:
异步代码是什么
我们的js代码主要分为两大类,同步代码和异步代码
同步代码是从上往下立即执行的,能马上看到执行结果,比如console
异步代码会先放在一个任务队列里,等同步代码执行完毕,再来执行异步代码
例如ajax请求、setTimeout()定时器、fs.readFile() 读取文件等都是异步代码
Promise是什么:
Promise 是 一个 构造函数, 用于创建Promise对象
Promise对象:可以理解为一个处理异步操作的容器
Promise作用:
解决回调地狱
Promise使用流程:
(1)创建promise实例
const p1 = new Promise( (resolve,reject)=>{ //异步代码 } )
(2)调用promise实例的then方法 p1.then( res=>{}).catch( err=>{} )
Promise原理:
(1)promise对象有三种状态 进行中(pending) 已成功(fulfilled) 已失败(rejected)
(2)promise状态只会有两种变化 从pending变为fulfilled 从pending变为rejected
(3)promise对象只要声明, 里面的异步会立即执行。 一般不会在promise内部去处理异步结果,而是调用resolve()或reject()
注意点
(1)promise并不能改变异步的顺序(异步是无序的,编译器决定无法改变), promise本质是控制异步结果的顺序
(2)promise本身只是一个容器,真正异步的是它的两个回调resolve()和reject()
现在我们再来了解Promise是怎么解决回调地狱的:
promise解决回调地狱原理 : 在上一个promise的then方法中, 返回下一个promise实例
fs.readFile() 读取文件是异步代码,我们用它来举个例子
const fs = require('fs')
fs.readFile(`${__dirname}/data/a.txt`, 'utf-8', (err, data) => {
if (err) {
throw err
} else {
console.log(data)
fs.readFile(`${__dirname}/data/b.txt`, 'utf-8', (err, data) => {
if (err) {
throw err
} else {
console.log(data)
fs.readFile(`${__dirname}/data/c.txt`, 'utf-8', (err, data) => {
if (err) {
throw err
} else {
console.log(data)
fs.readFile(`${__dirname}/data/d.txt`, 'utf-8', (err, data) => {
if (err) {
throw err
} else {
console.log(data)
}
})
}
})
}
})
}})
我们之前想按顺序读取data文件夹上的a,b,c文件,就要一层层的嵌套,等上一层的同步代码console.log(data)执行结果出来后再往下执行异步代码fs.readFile(),一层层嵌套
我们现在用Promise来解决这一问题
我们可以先封装一个函数
function createPromise(filename) {
return new Promise((resolve, reject) => {
//异步操作
fs.readFile(`${__dirname}/data/${filename}.txt`, "utf8", (err, data) => {
if (err) {
//失败
reject(err)
} else {
resolve(data)
}
})})}
声明调用
const p1 = createPromise('a')
const p2 = createPromise('b')
const p3 = createPromise('c')
用Promise的then方法
p1.then(res => {
//开始读取a
console.log(res)
//返回p2
return p2
})
.then(res => {
//p2的then
//开始读取b
console.log(res)
//返回p3
return p3
})
.then(res => {
//p3的then
//开始读取c
console.log(res)
})
可能有人会说那这跟上面的回调地狱有什么区别啊,不还是在嵌套吗?是的,promise虽然解决了异步回调地狱(回调函数层次嵌套)的问题,但是写起来的时候仍然需要嵌套
(链式语法嵌套,需要在上一个promise对象的then方法中返回下一个promise),那我们再了解一下async函数
ES6异步函数async与await
ES2017中引入的更为高级的异步处理机制,async函数,可以让异步的处理变的更加便捷
一句话概括: async函数相当于是promise异步函数的另一种高级写法
async语法如下
-
(1)函数前面使用
async修饰 -
(2) 函数内部,promise操作使用
await修饰await后面是promise对象, 左侧的返回值就是这个promise对象的then方法中的结果await必须要写在async修饰的函数中,不能单独使用,否则程序会报错
-
(3)
async函数内部的异常需要通过try,catch来捕获
上面的列子我们可以这样写:
const fs = require('fs')
function createPromise(filename) {
return new Promise((resolve, reject) => {
//异步操作
fs.readFile(`${__dirname}/data/${filename}.txt`, "utf8", (err, data) => {
if (err) {
//失败
reject(err)
} else {
resolve(data)
}
})})}
async function fn(){
const res1 = await createPromise('a')
console.log(res1)
const res2 = await createPromise('b')
console.log(res2)
//async异步函数的错误信息要用try-catch来捕捉
try {
const res3 = await createPromise('c')
console.log(res3)
} catch (err) {
console.log(err)
}
}
fn()
注意点:
1、async和await异步函数 : 这两个关键字只能用于函数, 所以用的时候一定要放在函数里面用
2、async关键字: 修饰函数。 表示这个函数内部有异步操作。
3、await关键字: 等待异步执行完毕。
4、await只能用于被async修饰的函数中。 只有当await后面的异步操作执行完毕后,才会继续执行后面代码
5、await 后面 只能是promise对象