深刻理解 promise、generator+co、async/await 用法

回调函数由于涉及的内容多而杂,而且在项目中也不怎么使用,因此在这里就先不说了,node

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

由于里面内容会有点多,而且还有好多代码示例。因此须要静下心慢慢看,相信看完以后,你确定会对这三种方法涉及的异步问题的理解更上一层楼面试

若是想要大体了解一下的话,能够看看个人这篇文章《JS中的异步解决方案》npm

我们先说Promise,而后慢慢涉及到其余,按部就班(其实这是JS处理异步的一个发展流程)数组

开始吧!!!promise

Promise

Promise简单的说就是一个容器,里面保存着某个将来才会结束的时间(一般是一个异步操做)的结果。从语法上说,Promise就是一个对象,从它能够获取异步操做的消息。Promise提供统一的API,各类异步操做均可以用一样的方法处理。bash

如何理解:异步

  • 没有异步就不须要promise
  • promise自己不是异步,只是咱们去编写异步代码的一种方式

promise中所谓的 4 3 2 1

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

  • 解决(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 的执行器中须要传入两个参数,分别是 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 这个 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

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方法,调用一次就会继续向下执行,直到遇到下一个yieldreturn
  • 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返回结果后,在继续执行

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

一般到公司面试的时候,面试官都会问到:

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

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

好了本章就先到此结束了,咱们下期再见


^_<

相关文章
相关标签/搜索