深入理解 promise、generator+co、async/await 用法
回调函数因为涉及的内容多而杂,并且在项目中也不怎么使用,所以在这里就先不说了,
本章重点讲解一下 Promise、generator + co、async/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) (也叫失败态)
 
针对每一种状态,有一些规范:
等待态(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 的执行器中需要传入两个参数,分别是 resolve 和 reject ,在内部调用时,就分别代表状态由 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 对象中调用 resolve 和 reject 时执行的函数
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 中的方法什么时候调用呢?
只有当调用 resolve,reject 时才会去执行 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.txt 和 age.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,会作为下一个 then 的 promise 对象,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
如果在 resolve 或 reject 中又是一个 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 类上面,提供了几个静态方法:
resolverejectfinallyallraceresolve
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.txt 和 age.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 库可以实现自动迭代
既然是自动执行,那么 promise 的 executor 中先执行一次 it.next() 方法,返回 value 和 done;value是一个 pending 的 Promise;如果 done=false,说明还没有走完,继续在 value.then 的成功回调中执行下一次 next,即调用 read 方法;直到 done 为 true,走完所有代码,调用 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 函数中使用
通常 async、await 都是跟随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对象await等promise返回结果后,在继续执行
这三种方法都是用来解决异步的,很很很重要
通常到公司面试的时候,面试官都会问到:
- 说一下异步的发展流程
 - 说一下异步的解决方案
 
那么看完本章内容就派上大用场了哦!
            
        