回调函数因为涉及的内容多而杂,并且在项目中也不怎么使用,所以在这里就先不说了,

本章重点讲解一下 Promisegenerator + coasync/await

因为里面内容会有点多,并且还有好多代码示例。所以需要静下心慢慢看,相信看完之后,你肯定会对这三种方法涉及的异步问题的理解更上一层楼

如果想要大致了解一下的话,可以看看我的这篇文章《JS中异步处理方案》

咱们先说 Promise,然后慢慢涉及到其他,循序渐进(其实这是JS处理异步的一个发展流程)

开始吧!!!

Promise


Promise 简单的说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作)的结果。从语法上说,Promise 就是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法处理。

如何理解:

  • 没有异步就不需要 promise
  • promise 本身不是异步,只是我们去编写异步代码的一种方式

promise 有所谓的 4 3 2 1


4 大术语
一定要结合异步操作来理解
既然是异步,这个操作需要有个等待的过程,从操作开始,到获取结果,有一个过程的

  • 解决(fulfill)指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之
  • 拒绝(reject)指一个 promise 失败时进行的一系列操作
  • 终值(eventual value)所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)
  • 据因(reason)也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值

3 种状态
在异步操作中,当操作发出时,需要处于等待状态
当操作完成时,就有相应的结果,结果有两种:

  • 成功了
  • 失败了

一共是 3 种状态,如下:

  • 等待态(Pending (也叫进行态)
  • 执行态(Fulfilled)(也叫成功态)
  • 拒绝态(Rejected) (也叫失败态)

2024/08/22/1724302786447.webp

针对每一种状态,有一些规范:

等待态(Pending)
处于等待态时,promise 需满足以下条件:

  • 可以迁移至执行态或拒绝态

执行态(Fulfilled)
处于执行态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的终值

拒绝态(Rejected)
处于拒绝态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的据因

2 种事件
针对 3 种状态,只有如下两种转换方向:

  • pending → fulfilled
  • pendeing → rejected

在状态转换的时候,就会触发事件:

  • 如果是pending → fulfiied,就会触发 onFulFilled 事件
  • 如果是pendeing → rejected,就会触发 onRejected 事件

在调用 resolve 方法或者 reject 方法的时候,就一定会触发事件
需要注册 onFulFilled 事件 和 onRejected 事件
针对事件的注册,Promise 对象提供了 then 方法,如下:
promise.then(onFulFilled,onRejected)

针对 onFulFilled,会自动提供一个参数,作为终值(value)
针对 onRejected,会自动提供一个参数,作为据因(reason)

1 个对象

promise

注:只有异步操作的结果,可以决定当前是哪一种状态,任何其他的操作都无法改变这个状态

基本使用


当我们创建 promise 时,会默认的处于 Pending 状态,并且在创建的时候,promise 中一定要有一个执行器,并且这个执行器会立即执行

// ()=>{} 叫执行器,会立即执行
let p = new Promise(()=>{ })
// 刚创建的 Promise 默认处理 Pending 状态
console.log(p); // Promise { <pending> }

promise 的执行器中需要传入两个参数,分别是 resolvereject ,在内部调用时,就分别代表状态由 pending=>fulfilled(成功) pending=>rejected(失败)

并且一旦 promise 状态发生变化之后,之后状态就不会再变了。比如:调用 resolve 之后,状态就变为 fulfilled,之后再调用 reject,状态也不会变化

let p = new Promise((resolve,reject)=>{
    resolve("有钱了....")  // 现在 promise 就处理成功态
})
console.log(p); // Promise { '有钱了....' }
// 失败态就不演示了

切记状态发生变化之后,之后状态就不会再变了

// 一个 promise 的状态只能从等待到成功,或从等待到失败
let p = new Promise((resolve,reject)=>{
    resolve("有钱了...")  // 成功了....
    reject("没钱了...")  // 失败了....
})
p.then(()=>{
    console.log("成功了....")
},()=>{
    console.log("失败了...")
})
// 只能输出  成功了...

then 方法


上面代码已经看到了,在使用时可以通过 promise 对象的内置方法 then 进行调用,then 有两个函数参数,分别表示 promise 对象中调用 resolvereject 时执行的函数

let p = new Promise((resolve,reject)=>{
    // resolve("有钱了...")  // 成功了....
    reject("没钱了...")  //失败了....
})
// 在 then 方法中,有两个参数
// 第一个参数表示从等待状到成功态,会调用第1个参数
// 第二个参数表示从等待状到失败态,会调用第2个参数
p.then(()=>{
    console.log("有钱了....")
},()=>{
    console.log("没钱了...")
})
// 输出结果 没钱了...

在执行完后,成功肯定有一个成功的结果 失败肯定有一个失败的原因,那么如何得到成功的结果 ? 如何得到失败原因呢?

let p = new Promise((resolve,reject)=>{
    // 调用 reolve 时,可以把成功的结果传递下去
    // resolve("有钱了...")  // 成功了...
    // 调用 reject 时,可以把失败的原因传递下去
    reject("没钱了...")  // 失败了....
})
p.then((suc)=>{
    console.log(suc)
},(err)=>{
    console.log(err)
})
// 输出结果  没钱了...

当我们在执行失败处理时,也可以用 throw,就是抛出一个错误对象,也是失败的

如下:

let p = new Promise((resolve,reject)=>{
    // throw 一个错误对象  也是失败的
    throw new Error("没钱了...")
})
p.then((suc)=>{
    console.log(suc);
},(err)=>{
    console.log(err);
})

throw 的定义:throw 语句用来抛出一个用户自定义的异常。当前函数的执行将被停止(throw 之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个 catch 块。如果调用者函数中没有 catch 块,程序将会终止。

// 尝试一下
function getRectArea(width, height) {
  if (isNaN(width) || isNaN(height)) {
    throw "Parameter is not a number!";
  }
}
try {
  getRectArea(3, 'A');
}
catch(e) {
  console.log(e);
  // expected output: "Parameter is not a number!"
}

promise 本身是同步的

// promise 本身是同步的
console.log("start")
let p = new Promise(()=>{
    console.log("哈哈")  // 哈哈
})
console.log("end")  
// 输出顺序   start  哈哈   end

并且在执行器的内部也是可以写异步代码的

那么 then 中的方法什么时候调用呢?
只有当调用 resolvereject 时才会去执行 then 中的方法

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log("setTimeout")
        // resolve("有钱了...")
        reject("没钱了...")
    },1000)
})
p.then((suc)=>{
    console.log(suc)  // 有钱了...
},(err)=>{
    console.log(err)  // 没钱了...
})

链式调用(重点)


先我们在目录下面建两个文件,分别是:name.txtage.txt
name.txt 文件里写了一个 age.txt
age.txt 文件里写了一个 666

下面就以读取文件为例,来演示链式调用(需要了解一点 node 基础)

当你读取文件的时候,如果你用的是 vscode 编辑器,里面会有一个小 bug,用相对路径可能会出错,所以最好使用绝对路径

读取文件:

let fs = require("fs")
let path = require("path")
let filename = path.join(__dirname,"name.txt")
fs.readFile(filename,"utf8",(err,data)=>{
    if(err){
        console.log(err)
    }
    fs.readFile(path.join(__dirname,data),"utf8",(err,data)=>{
        if(err){
            console.log(err)
        }
        console.log(data)
    })
})
// 输出结果 666

如果用这种方法,就会出现 回调地狱 ,很难受,所以一般不用

在读取文件时,我们可以专门 封装一个函数 ,功能就是读取一个文件的内容

let fs = require("fs")
// 封装一个函数,函数的功能是读取一个文件的内容
// rest 参数(下去自己了解一下,就是可以获取到传过来的所有内容)  
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
// 读文件
readFile("./name.txt","utf8").then(data=>{
    console.log(data)   
},err=>{
    console.log(err)  
})
// 输出结果  age.txt

如果文件不存在,会走第二个函数

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
// 如果 name1 不存在,走 then 的第 2 个函数
readFile("./name1.txt","utf8").then(data=>{
    console.log(data)  
},err=>{
    console.log(err)   
})
// 报错  no such file or directory

那么如果我们想要读取 age.txt 里面的内容呢?
我们可以这么写:

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
 
readFile("./name.txt","utf8").then(data=>{
    // console.log(data)  // age.txt
    readFile(data,"utf8").then(data=>{
        console.log(data)  // 666
    },err=>{
        console.log(err)  
    })
},err=>{
    console.log(err)  
})
// 输出结果  666

这样写就可以获取到 age.txt 文件里面的内容,但是呢,这样写又回到了 回调地狱 ,不是说这种方法不行,而是不够优雅

使用 链式调用
promise 中可以链式调用 就是 .then 之后,还可以 .then ,你可以无数次的 .then
.then 之后又返回了一个新的 promise,就是 .then 的函数参数中会默认返回 promise 对象,所以当你碰到 .then 连续调用的时候,你就可以把前面的所有代码当成一个 promise

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}

readFile("./name.txt","utf8").then(data=>{
    // console.log(data)  // age.txt
    return false;
},err=>{
    console.log(err)  
}).then(data=>{  // 这里面的 data 是上一个 then 中的第一个函数的返回值,这个 .then 前面的一坨代码就可以当成一个promise
    console.log(data)  // false
},err=>{

})

如果没有这个文件,则返回错误信息

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}
readFile("./name1.txt","utf8").then(data=>{
    return data;
},err=>{
    return err;
    // console.log(err)  
}).then(data=>{  
    console.log(data)
},err=>{
    console.log(err)
})
// 输出结果   no such file or directory

但是如果我们返回一个 promise 呢?
那么这个 promise 会执行,并且会采用他的状态

let fs = require("fs")
function readFile(...args){
    return new Promise((resolve,reject)=>{
        fs.readFile(...args,function(err,data){
            if(err) reject(err)
            resolve(data)
        })
    });
}

readFile("./name.txt","utf8").then(data=>{
    return data;
},err=>{
    return err;
    // console.log(err)  
}).then(data=>{  
    // console.log(data)
    return new Promise((resolve,reject)=>{	//返回一个promise
        reject("不OK")	//下面的.then采用这个状态(失败态)
    })
},err=>{}).then(data=>{
    console.log(data)
},err=>{
    console.log(err)  // 不OK
})
// 输出结果  不OK

所以如果返回的是一个 promise,那么这个 promise 会执行,并且会采用它的状态

小总结:

如果在上一个 then 的第一个函数中,返回一个普通值,那么无论你是在第 1 个函数中返回,还是在第 2 个函数中返回,都会作为下一个 then 的成功的结果,如果不返回,undefined 就作为下一个 then 的成功的结果

如果返回的是一个 promise,会作为下一个 thenpromise 对象,data err 去 promise 对象中去取,也就是说,前一个 then 的返回值,会作为后一个 then 的参数

再给两个小例子,自己看一下:

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("hello")
    },1000)
})
p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve("world")
        },1000)
    })
}).then(data=>{
    console.log(data)  
},err=>{

})
// 输出结果 world
let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject("不OK")
        },1000)
    })
})
p1.then(data=>{
    console.log(data)  
},err=>{
    console.log(err) 
})
// 输出结果   不OK

一个坑(循环引用)

接下来说一个小问题,链式调用中的 循环引用

有的人不喜欢把前面的一大堆代码后面加 .then,所以就用了下面的一种写法(有可能会出现 循环引用):

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})

let p1 = p.then(data=>{
    return p1; // 循环引用 报错 p1 在等 p1 的状态改变,也就是我在等我吃饭,显然是不行的
})

p1.then(data=>{
    console.log(data)
},err=>{
    console.log("-----",err); // 可执行,然后报错
})

如果我们把状态确定住,那就可以了

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    // return 123  相当于把等待态改变成成功态
    return 123
})
p1.then(data=>{
    console.log(data); // 123
},err=>{
    console.log("-----",err);
})
// 输出 123

当然改变成失败态也可以

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    // return new Error("不OK") 把等待态变成失败态
    return new Error("不OK")
})
p1.then(data=>{
    console.log(data) 
},err=>{
    console.log(err); // Error: 不OK
})

递归解析


看一个问题

let p = new Promise((resolve,reject)=>{
    resolve("hello")
})
let p1 = p.then(data=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    resolve("666")
                },1000)
            }))
        },1000)
    })
})
// data 是 promise 还是 666
p1.then(data=>{
    console.log(data)  
},err=>{
    console.log(err) 
})

按理说,data 打印出来的是一个 promise,就是上面 resolve 里面的一堆代码
然而并不是,promise 会进行递归解析,到最后上面代码会打印出来 666

如果在 resolvereject 中又是一个 promise,那么就会递归解析(无论有多少个 promise

let p = new Promise((resolve, reject) => {
    resolve("hello")
})
let p1 = p.then(data => {
    return new Promise((resolve, reject) => {
        resolve(new Promise((resolve, reject) => {
            resolve(new Promise((resolve, reject) => {
                resolve(new Promise((resolve, reject) => {
                    resolve(new Promise((resolve, reject) => {
                        resolve(new Promise((resolve, reject) => {
                            resolve("666")
                        }))
                    }))
                }))
            }))
        }))
    })
})
p1.then(data => {
    console.log(data)
}, err => {
    console.log(err)
})
// 打印结果  666

catch 方法


  • catch 方法,用于注册 onRejected 回调
  • catch 其实是 then 的简写,then(null,callback)

catch 就是 .then 的语法糖

如果 .then 中有第 2 个函数,在这个 .then 后面又有 catch,如果到失败态,那么会走 .then 的第 2 个函数

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})

p.then(data=>{

},err=>{
    console.log("1",err)
}).catch(err=>{
    console.log("2",err)
})
// 输出结果 1 不OK

如果 .then 中没有第 2 个函数,在这个 .then 后面又有 catch,如果到失败态,那么会走 catch

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})
p.then(data=>{

}).catch(err=>{
    console.log("2",err)
})
// 输出结果  2 不OK

一个坑

.then 第二个函数中,return err 它是 return 到了下一个.then 的第一个函数中

let p = new Promise((resolve,reject)=>{
    reject("不OK")
})
p.then(data=>{

},err=>{
    // 在这里它并没有 reutrn 到 err 中,它 reutrn 到第一个参数中
    return err
}).then(data=>{
    console.log("data----",data)
},err=>{
    console.log("err----",err)
})
// 输出结果 data---- 不OK

所以最终:
一个 promise 中,一般在 then 中只有一个函数,在 then 后面有一个 catch,一般使用 then 来获取 data,在 catch 中获取 err

let p = new Promise((resolve,reject)=>{
    resolve("OK")
})

p.then(data=>{
    console.log(data)
}).catch(err=>{
    console.log(err)
})

静态方法


Pomise 类上面,提供了几个静态方法:

  • resolve
  • reject
  • finally
  • all
  • race
  • resolve

resolve

Promise.resolve("有钱了...").then(data=>{
     console.log(data)  // 有钱了...
})

等价于下面这种写法:

let p = new Promise((resolve,reject)=>{
    resolve("有钱了...")
})
p.then(data=>{
    console.log(data)
})

reject

Promise.reject("没钱了...").catch(data=>{
    console.log(data)  // 没钱了...
})

finally

不管转成成功态还是失败态,都会调用finally这个方法

Promise.resolve("有钱").then(data=>{
    console.log(data)
}).finally(()=>{
    console.log("开心...")
})
// 打印结果  有钱   开心...
Promise.reject("没钱").catch(data=>{
    console.log(data)
}).finally(()=>{
    console.log("不开心...")
})
// 打印结果  没钱   不开心...

all

all 表示 [ ] 中的 promise 都成功了,才能得到最终的值

注意里面是一个数组
读取 name.txtage.txt 中的内容

let fs = require("fs").promises;
// all 表示 [] 中的 promise 都成功了,才能得到最终的值
Promise.all([fs.readFile("./name.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data) // [ 'age.txt', '666' ]
})
// 打印结果  [ 'age.txt', '666' ]

如果有一个不成功,那么就不行

let fs = require("fs").promises;
Promise.all([fs.readFile("./name1.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data)
})
// 这个是不行的

race

顾名思义,race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3]) 里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let fs = require("fs").promises;
Promise.race([fs.readFile("./name.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
    console.log(data) // age.txt
})

改变文件里面的内容,多尝试几次

generator + co


我很佩服你能看到这里,厉害

先说 生成器 和 迭代器

生成器可以生成迭代器,可以让程序中断,不会把 { } 中的代码全部执行

用法:在 function 和自己声名的名称之间加一个 * 号,里面用 yield
产出数据,然后调用生成器生成迭代器

function * read(){
    yield 1;  // 只有产出,并不执行
}
// 调用生成器 生成 迭代器   it 就是迭代器  
let it = read();

生成器可以产出很多值,迭代器只能 next 一下,拿一个值,next 一下,拿一个值

function * read(){
    yield 1;  
}
let it = read()
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: undefined, done: true }
function * read(){
    yield 1;  
    yield 2;
    yield 3;
}
// 调用 read()  返回值是迭代器
let it = read()
console.log(it.next());  // { value: 1, done: false }
console.log(it.next());  // { value: 2, done: false }
console.log(it.next());  // { value: 3, done: false }
console.log(it.next());  // { value: undefined, done: true }

如果 next 中有参数的话,那么他会把这个参数传给上一个生成器声明的变量里

所以第一个 next 中的参数没有任何意义,我们一般不写

function * read(){
    let a = yield 1; 
    console.log(a)    // 9
    let b = yield 2;
    console.log(b)    // 10
    let c = yield 3;
    console.log(c)   // 11
}
let it = read()
console.log(it.next())   // { value: 1, done: false }
console.log(it.next(9))    // { value: 2, done: false }
console.log(it.next(10))   // { value: 3, done: false }
console.log(it.next(11))   // { value: undefined, done: true }

接下来用这个实现我们的读文件操作,哈哈,是不是很恶心

读取 name.txt 文件

const fs = require("fs").promises;
// 生成器
function * read(){
    yield fs.readFile("./name.txt","utf-8");
}
// 迭代器
let it = read();
// console.log(it.next()); // { value: Promise { <pending> }, done: false }
it.next().value.then(data=>{ // 因为是一个对象,所以直接 .value
    console.log(data);
})
// 输出结果 age.txt

然后读取 age.txt 文件

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8")
    yield fs.readFile(concent,"utf-8")

}
let it = read()
it.next().value.then(data=>{
    // console.log(data)  
    // console.log(it.next(data)) // { value: Promise { <pending> }, done: false }
    it.next(data).value.then(data=>{
        console.log(data)  
    })
})
// 输出结果 666

也可以这样

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8")
    let age = yield fs.readFile(concent,"utf-8")
    return age
}
let it = read()
it.next().value.then(data=>{
    it.next(data).value.then(data=>{
        let r = it.next(data)
        console.log(r); // { value: '666', done: true }
    })
})

是不是感觉又陷入了 回调地狱

那么就用 co 吧

安装 co npm i co

用上来:

const fs = require("fs").promises;
function * read(){
    let concent = yield fs.readFile("./name.txt","utf-8");
    let age = yield fs.readFile(concent,"utf-8");
    return age;
}
let co = require("co")
co(read()).then(data=>{
    console.log(data); 
})
// 输出结果  666

是不是简单多了,爽不爽

co 库可以实现自动迭代
既然是自动执行,那么 promiseexecutor 中先执行一次 it.next() 方法,返回 valuedonevalue是一个 pendingPromise;如果 done=false,说明还没有走完,继续在 value.then 的成功回调中执行下一次 next,即调用 read 方法;直到 donetrue,走完所有代码,调用 resolve;中间有任何一次 next 异常,直接调用 reject,停止迭代

总结:

  • function 关键字与函数名之间有一个星号
  • 函数体内部使用 yield 语句,定义不同的内部状态
  • yield 会将函数分割成好多个部分,每产出一次,就暂停一次
  • Genenrator 是一个生成器,调用 Genenrator 函数,不会立即执行函数体,只是创建了一个迭代器对象,如上例中的 it 就是调用 read 这个 Generator 函数得到的一个迭代器
  • 迭代器有一个 next 方法,调用一次就会继续向下执行,直到遇到下一个 yield 或 `return
  • next() 方法可以带一个参数,该参数会被当做上一条 yield 语句的返回值,并赋值给 yield 前面等号前的变量
  • 每遇到一个 yield,就会返回一个 {value:xxx,done:bool} 的对象,然后暂停,返回的 value 就是跟在 yield 后面的返回值,done 表示这个 generator 是否已经执行结束了
  • 当遇到 return 时,return 后的值就是 value 值,done 此时就是 true
  • 函数末尾如果没有 return,就是隐含的 return undefined
  • 使用 co 库,可以自动的将 generator 迭代
  • co 执行会返回一个 promise,用 then 注册成功/失败回调
  • co 将迭代器 it 作为参数,这里每调用一次 read,就执行一次 next

async/await


被称为 异步解决 的终极方案

async、await 是什么?

async 顾名思义是 “异步” 的意思,async 用于声明一个函数是异步的。

await 从字面意思上是 “等待” 的意思,就是用于等待异步完成。通俗来说,就是 await 在这里等待 promise 返回结果了,再继续执行。并且 await 只能在 async 函数中使用

通常 asyncawait 都是跟随Promise一起使用的。
为什么这么说呢?因为 async 返回的都是一个 Promise 对象,同时 async 适用于任何类型的函数上。这样 await 得到的就是一个 Promise 对象(如果不是 Promise 对象的话那 async 返回的是什么就是什么);
注:await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的(不演示了)

紧跟着上面的代码,再写一段

const fs = require("fs").promises;
async function read(){
    let concent = await fs.readFile("./name.txt","utf-8")
    let age = await fs.readFile(concent,"utf-8")
    return age
}
read().then(data=>{
    console.log(data)   // 666
})

是不是比上面的写法还爽呢?

await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中

如下:

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise().catch(function (err){
    console.log(err);
  });
}

总结:

async:

  • async 函数会返回一个 Promise 对象
  • 如果 async 函数中是 return 一个值,这个值就是 Promise 对象中 resolve 的值
  • 如果 async 函数中是 throw 一个值,这个值就是 Promise 对象中 reject 的值

await:

  • await 只能在 async 函数中使用
  • await 后面要跟一个promise对象
  • awaitpromise 返回结果后,在继续执行

这三种方法都是用来解决异步的,很很很重要

通常到公司面试的时候,面试官都会问到:

  • 说一下异步的发展流程
  • 说一下异步的解决方案

那么看完本章内容就派上大用场了哦!