前端异步编程规范
一.Promise介绍
1.什么是Promise
Promise
,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大
在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码:
doSomething(function (result) {
doSomethingElse(result, function (newResult) {
doThirdThing(newResult, function (finalResult) {
console.log('得到最终结果: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
阅读上面代码,是不是很难受,上述形成了经典的回调地狱。
现在通过Promise
的改写上面的代码:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);
2.Promise 的用法
Promise
对象是一个构造函数,用来生成Promise
实例:
let p = new Promise((resolve, reject) => {});
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
:
resolve
函数的作用是,将Promise
对象的状态从 “未完成” 变为 “成功”;reject
函数的作用是,将Promise
对象的状态从 “未完成” 变为 “失败”;
3.Promise 的实例方法
Promise
构建出来的实例存在以下方法:
- then();
- catch();
- finally();
3.1 then()
then
是实例状态发生改变时的回调函数,第一个参数是回调函数resolve
,第二个参数是回调函数reject
。
then
方法返回的是一个新的Promise
实例,这个也就是Promise
能链式书写的原因。
3.2 catch()
catch()
方法是.then(null, reject)
或.then(undefined, reject)
的别名,用于指定发生错误时的回调函数。
在我们平时兼容接口返回错误的情况会经常使用:
getJSON('/posts.json').then(posts => {
// ...
}).catch(error => {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
Promise
对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止:
getJSON('/post/1.json').then(post => {
return getJSON(post.commentURL);
}).then(comments => {
// console.log(comments);
}).catch(error => {
// 处理前面三个Promise产生的错误
});
3.2 finally()
finally()
方法用于指定不管 Promise
对象最后状态如何,都会执行的操作。
new Promise((resolve, reject) => {...})
.then(res => {···})
.catch(error => {···})
.finally(() => {···});
4.Promise 构造函数方法
Promise
构造函数存在以下方法:
- all()
- race()
- allSettled()
- any()
这四个方法都是用来处理 Promise
实例数组的,我们后面手动封装时会详细说到,所以这里先略过。
想必看到这里,你一定很好奇这样一个神奇的函数是如何实现的,那就让我们来手动封装一个 自己的 Promise
吧!
二.简版Promise
1.resolve 和 reject
我们用 Promise
生成三个实例:
p1先执行resolve,然后再执行reject;
p2先执行reject,然后再执行resolve;
p3直接throw异常:
let p1 = new Promise((resolve, reject) => {
resolve('success')
reject('fail')
})
console.log('p1', p1)
let p2 = new Promise((resolve, reject) => {
reject('success')
resolve('fail')
})
console.log('p2', p2)
let p3 = new Promise((resolve, reject) => {
throw('error')
})
console.log('p3', p3)
我们来看下控制台的打印结果:
这里说明了Promise
的四个特点:
- 执行了 resolve,Promise 状态会变成 fulfilled;
- 执行了 reject,Promise 状态会变成 rejected;
- Promise 状态不可逆,第一次成功就永久为 fulfilled,第一次失败就永远状态为 rejected;
- Promise 中有 throw 的话,就相当于执行了 reject。
1.1 实现 resolve 和 reject
- Promise 需要接受两个回调函数:resolve 和 reject;
- Promise 需要一个状态值PromiseState和一个返回结果PromiseResult,PromiseState 初始状态是pending,PromiseResult初始值是 null;
- Promise 需要对 resolve 和 reject 绑定this:确保 resolve 和 reject 的 this 指向永远指向当前的
MyPromise
实例,防止随着函数执行环境的改变而改变; - 根据执行 resolve() / reject(),改变 PromiseState 和 PromiseResult 的值,即完成了我们
MyPromise
的一个初步效果。
代码如下:
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
// 执行传进来的函数
executor(this.resolve, this.reject)
}
// 初始化值
initValue() {
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
}
// 初始化this
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
}
reject(reason) {
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
}
}
测试如下:
let p1 = new MyPromise((resolve, reject) => {
resolve('success')
})
console.log(p1)
// MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }
let p2 = new MyPromise((resolve, reject) => {
reject('fail')
})
console.log(p2)
// MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
注意:
- 上述 MyPromise 使用 es6 构造函数的 class 语法声明;
我们对比下 es5 和 es6 两种构造函数的声明方式:
// es5:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log("I am ", this.name);
}
}
// es6:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
console.log("I am ", this.name);
}
}
excutor
是执行器,执行器就是一个函数;- 对于
initBind
绑定 this 指向需要详细说明一下;
有一些同学可能会有疑问,构造函数的 this 不是本来就指向它的实例吗?为什么还要手动再绑定一次?
对于这个问题我们先来打印下 initBind()
和 resolve()
里面的 this:
我们发现,如果不绑定this,执行到 resolve 方法时,this 指向了 undefined。
我们来看一下 resolve() 的调用位置:
执行到 resolve('success')
时,即在执行 executor 的时候,p1 中还没有将 MyPromise
中拥有的成员方法继承过来,即 p1 上还没有这个方法,但是构造函数上有这个方法,所以我们需要提前手动绑定this,让实例的方法绑定到构造函数上。
1.2 状态不可变
let p1 = new MyPromise((resolve, reject) => {
resolve('success')
reject('fail')
})
console.log(p1)
// MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
正确的应该是状态为fulfilled,但这里状态又变成了rejected。
Promise有三种状态:
pending
:等待中,是初始状态;fulfilled
:成功状态;rejected
:失败状态;
一旦状态从 pending
变为 fulfilled
或者 rejected
,那么此Promise实例的状态就不可以改变了。
这步只需要:
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
this.PromiseState = 'fulfilled'
this.PromiseResult = value
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = reason
}
也就是:
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
// 执行传进来的函数
executor(this.resolve, this.reject)
}
// 初始化值
initValue() {
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
}
// 初始化this指向
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
}
}
结果如下:
let p2 = new MyPromise((resolve, reject) => {
// 只以第一次为准
resolve('success')
reject('fail')
})
console.log(p2) //
MyPromise { PromiseState: 'fulfilled', PromiseResult: 'success' }
1.3 throw
Promise 中有 throw 的话,就相当于执行了reject:
这个地方在代码中就要使用try catch了。
1.3.1 try catch
不管你多么精通编程,有时我们的脚本总还是会出现错误。可能是因为我们的编写出错,或是与预期不同的用户输入,或是错误的服务端响应以及其他数千种原因。
通常,如果发生错误,脚本就会“死亡”(立即停止),并在控制台将错误打印出来。
但是有一种语法结构 try...catch
,它使我们可以“捕获(catch)”错误,因此脚本可以执行更合理的操作,而不是死掉。
语法:try...catch
结构由两部分组成:try
和 catch
:
try {
//...
} catch (err) {
// 错误捕获
}
它按照以下步骤执行:
- 首先,执行
try {...}
中的代码; - 如果这里没有错误,则忽略
catch (err)
:执行到try
的末尾并跳过catch
继续执行; - 如果这里出现错误,则
try
执行停止,控制流转向catch (err)
的开头,变量err
(我们可以使用任何名称)将包含一个 error 对象,该对象包含了所发生事件的详细信息。
所以,try {...}
块内的 error 不会杀死脚本 —— 我们有机会在 catch
中处理它。
1.3.2 throw
我们将try…catch加到我们的核心代码中:
try {
// 执行传进来的函数
executor(this.resolve, this.reject)
} catch (e) {
// 捕捉到错误直接执行reject
this.reject(e)
}
完整代码为:
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
try {
// 执行传进来的函数
executor(this.resolve, this.reject)
} catch (e) {
// 捕捉到错误直接执行reject
this.reject(e)
}
}
// 初始化值
initValue() {
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
}
// 初始化this指向
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
}
}
测试代码:
let p3 = new MyPromise((resolve, reject) => {
throw('fail')
})
console.log(p3)
// MyPromise { PromiseState: 'rejected', PromiseResult: 'fail' }
2.then
平时业务中 then
的使用一般如下:
// 马上输出 'success'
let p1 = new Promise((resolve, reject) => {
resolve('success')
}).then(res => console.log(res), err => console.log(err))
// 1秒后输出 'fail'
lett p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {
resolve(100)
}).then(res => 2 * res, err => console.log(err))
.then(res => console.log(res), err => console.log(err))
根据上述代码可以确定:
- then 接收两个回调,一个是成功回调,一个是失败回调;
- 当 Promise 状态为 fulfilled 时,执行成功回调;为 rejected 时,执行失败回调;
- 如 resolve 或 reject 在定时器里,则定时器结束后再执行 then;
- then 支持链式调用,下一次 then 执行受上一次 then 返回值的影响。
2.1 实现then
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
onRejected(this.PromiseResult)
}
}
完整代码为:
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
try {
// 执行传进来的函数
executor(this.resolve, this.reject)
} catch (e) {
// 捕捉到错误直接执行reject
this.reject(e)
}
}
// 初始化值
initValue() {
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
}
// 初始化this指向
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
}
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
onRejected(this.PromiseResult)
}
}
}
测试代码:
// 输出 'success'
let p = new MyPromise((resolve, reject) => {
resolve('success')
}).then(res => console.log(res), err => console.log(err))
2.2 定时器
// 1秒后输出 'fail'
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
我们用刚刚封装的 MyPromise 去模拟一下上面的定时器的案例:
// 控制台没有打印
let p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
}).then(res => console.log(res), err => console.log(err))
我们发现控制台没有打印任何结果。
这是因为代码执行到then的时候, resolve 和 reject还没有执行,PromiseStatus 仍然为 pending
,而then里面没有pending
对应的执行逻辑。
那如何保证代码能够在1s
后,执行then的回调?
我们不能确保1s
后才执行then函数,但是我们可以保证1s
后再执行then里的回调。
在这1s时间内,我们可以先把then里面的两个回调保存起来,然后等到1s过后,执行了 resolve 或者 reject,再去判断状态,根据状态判断要去执行刚刚保存的两个回调中的哪一个回调。
那么问题来了,我们怎么知道当前1s还没走完甚至还没开始走呢?
其实很好判断,只要状态是 pending
,那就证明定时器还没跑完,因为如果定时器跑完的话,那状态肯定就不是pending
,而是fulfilled
或者rejected
。
那用什么来保存这些回调呢?
建议使用数组,因为一个promise实例可能会多次then,用数组就一个一个保存起来就可以了。
initValue() {
// 初始化值
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
+ this.onFulfilledCallbacks = [] // 保存成功回调
+ this.onRejectedCallbacks = [] // 保存失败回调
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
// 执行保存的成功回调
+ while (this.onFulfilledCallbacks.length) {
+ this.onFulfilledCallbacks.shift()(this.PromiseResult)
+ }
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
// 执行保存的失败回调
+ while (this.onRejectedCallbacks.length) {
+ this.onRejectedCallbacks.shift()(this.PromiseResult)
+ }
}
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.PromiseResult)
+ } else if (this.PromiseState === 'pending') {
+ // 如果状态为待定状态,暂时保存两个回调
+ this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+ this.onRejectedCallbacks.push(onRejected.bind(this))
+ }
}
完整代码为:
看下是否能够实现定时器的功能:
2.3 链式调用
then支持链式调用,下一次then执行受上一次then返回值的影响,给大家举个例子:
根据上文,可以得到:
- then方法本身会返回一个新的Promise对象;
- 如果返回值是promise对象,返回值为成功,新promise就是成功;
- 如果返回值是promise对象,返回值为失败,新promise就是失败;
- 如果返回值非promise对象,新promise对象就是成功,值为此返回值;
then是Promise上的方法,那如何实现then完还能再then呢?
then执行后返回一个Promise对象就行了,就能保证then完还能继续执行then。
我们要在then里面调用 onFulfilled 和 onRejected 的方法,此时我们创建一个 resolvePromise
作为thenPromise 和 onfulfilled/onRejected 建立的关联。
使用 resolvePromise
调用 onfulfilled/onRejected 方法,需要把 onfulfilled/onRejected 作为参数传进去,onfulfilled(this.PromiseResult)/onRejected(this.PromiseResult)调用后再判断其得到的结果,根据结果继续往下走。
- 如果也是一个Promise对象,就去执行then;
- 如果只是一个值,就resolve;
- 如果异常,就执行reject;
对于resolvePromise
来说,会存在一些边界条件,比如返回自身时,可能会造成死循环,所以要抛出异常:
then的完整代码为:
测试一下:
2.4 执行顺序
这里需要了解,then方法是微任务,微任务应在宏任务执行后才执行(后续文章会讲到)。
我们来看一下Promise的例子:
这里为了实现类似的功能,我们先使用setTimeout代替(setTimeout为宏任务,此处主要跟在全局上的console对比)
至此,完整的代码为:
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue()
// 初始化this指向
this.initBind()
try {
// 执行传进来的函数
executor(this.resolve, this.reject)
} catch (e) {
// 捕捉到错误直接执行reject
this.reject(e)
}
}
// 初始化值
initValue() {
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
this.onFulfilledCallbacks = [] // 保存成功回调
this.onRejectedCallbacks = [] // 保存失败回调
}
// 初始化this
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
// 执行保存的成功回调
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(this.PromiseResult)
}
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
// 执行保存的失败回调
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(this.PromiseResult)
}
}
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) {
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = cb => {
setTimeout(() => {
try {
const x = cb(this.PromiseResult)
// 不能返回自身哦
if (x === thenPromise && x) {
throw new Error('不能返回自身。。。')
}
// 如果返回值是Promise
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
throw new Error(err)
}
})
}
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
resolvePromise(onFulfilled)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
resolvePromise(onRejected)
} else if (this.PromiseState === 'pending') {
// 如果状态为待定状态,暂时保存两个回调
// 如果状态为待定状态,暂时保存两个回调
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
}
})
// 返回这个包装的Promise
return thenPromise
}
}
测试一下:
let p4 = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
// 输出顺序是 2 1
3.其它方法
3.1 all
Promise.all()
方法用于将多个 Promise
实例,包装成一个新的 Promise
实例。
语法:
Promise.all([p1, p2, p3]);
我们来使用一下all,并打印一下它的返回结果。
let p1 = new MyPromise((resolve, reject) => {
resolve('success')
});
let p2 = new MyPromise((resolve, reject) => {
resolve(100)
});
let p3 = new MyPromise((resolve, reject) => {
throw('error')
});
Promise.all([p1, p2, 2]).then(res => {
console.log(res)
})
// ['success', 100, 2]
Promise.all([p1, p2, p3]).then(res => {
console.log(res)
}, err => {
console.log(err)
})
// 'error'
作用:
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
- 如果所有Promise都成功,则返回成功结果数组;
- 如果有一个Promise失败,则返回这个失败结果;
如果我们想模拟封装一个这样的all
呢?
- 声明两个变量:
- result 用来存放结果;
- count 用来计数;
- 当 Promise 数组中的函数全部执行成功时,将result 返回;
- 依次调用 Promise 数组中的函数:
- 如果是 Promise 函数,就交给then,执行成功就存放在 result 中,失败就直接 reject;
- 如果不是 Promise 函数而是数值,就当作执行成功,直接把值存放在 result 中;
- 存储在 result 中后,每次需要判断一下,是否已经全部执行完成,若已全部执行完成,则将 result 返回;
all
的封装代码:
const all = (promiseList) => {
let result = [];
let count = 0;
return new Promise((resolve, reject) => {
const addData = function (val, i) {
result[i] = val;
count++;
// 若全部执行完成,则将结果返回
if (count === promiseList.length) {
resolve(result)
}
}
promiseList.forEach((p, i) => {
if (p instanceof MyPromise) {
p.then(res => {
// 将结果存放在 result 中
addData(res, i)
}, err => {
// 直接 reject
reject(err)
})
} else {
// 将结果存放在 result 中
addData(p, i)
}
})
})
}
3.2 race
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
语法:
Promise.race([p1, p2, p3]);
我们来使用一下race,并看一下它的返回结果。
let p1 = new MyPromise((resolve, reject) => {
resolve('success')
});
let p4 = new MyPromise((resolve, reject) => {
reject('fail')
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
let p5 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success is 2s delay')
}, 2000)
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
Promise.race([p1, p4, p5]).then(res => {
console.log(res)
})
// 'success'
作用:
- 接收一个 Promise 数组,数组中如有非Promise项,则此项当做成功;
- 哪个 Promise 最快得到结果,就返回那个结果,无论成功失败;
如果我们想模拟封装一个这样的race
呢?
思路基本和上面的 all 相同,甚至逻辑还比 all 要更简单。
因为 race
是抢占式的,所以依次调用 Promise 数组中的函数直接返回即可,先到先得。
- 如果是 Promise 函数,就交给then,执行成功直接 resolve,失败就直接 reject;
- 如果不是 Promise 函数而是数值,就当作执行成功,直接 resolve;
race
的封装代码:
const race = (promiseList) => {
return new Promise((resolve, reject) => {
promiseList.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}
3.3 allSettled
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。
只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束。
语法:
Promise.allSettled([p1, p2, p3]);
我们来使用一下allSettled,并打印一下它的返回结果。
let p1 = new MyPromise((resolve, reject) => {
resolve('success')
});
let p3 = new MyPromise((resolve, reject) => {
throw('error')
});
let p5 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success is 2s delay')
}, 2000)
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
Promise.allSettled([p1, p3, p5, 100]).then(res => {
console.log(res)
})
打印结果:
作用:
- 接收一个 Promise 数组,数组中如有非Promise项,则此项当做成功;
- 把每一个 Promise 的结果,集合成数组后返回;
如果我们想模拟封装一个这样的allSettled
呢?
思路和all
是一样的,区别是:
- 返回的 result 是json数组,需要包含状态值:
- 依次调用 Promise 数组中的函数时,即使失败,也要把结果存放在 result 中,而不是直接 reject;
allSettled
的封装代码:
const allSettled = (promiseList) => {
let result = [];
let count = 0;
return new Promise((resolve, reject) => {
const addData = (value, i, status) => {
result[i] = {
value,
status
}
count++;
if (count === promiseList.length) {
resolve(result);
}
}
promiseList.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(res, i, 'fulfilled')
}, err => {
// 失败也要存放在result中
addData(err, i, 'rejected')
})
} else {
addData(promise, i, 'fulfilled')
}
})
})
}
3.4 any
Promise.any()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。
语法:
Promise.any([p1, p2, p3]);
我们来使用一下any,并打印一下它的返回结果。
let p1 = new Promise((resolve, reject) => {
resolve('success')
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
let p6 = new Promise((resolve, reject) => {
reject('fail')
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
let p7 = new Promise((resolve, reject) => {
throw new Error('123')
})
Promise.any([p1, p5, p6]).then(res => {
console.log(res);
}, err => {
console.log(err);
})
// 打印结果:
// success
// fail
// undefined
Promise.any([p5, p6]).then(res => {
console.log(res);
}, err => {
console.log(err);
})
// 打印结果:
// undefined
作用:与Promise.all()
作用正相反。
1. 接收一个Promise数组,数组中如有非Promise项,则此项当做成功;
2. 如果有一个Promise成功,则返回这个成功结果;
3. 如果所有Promise都失败,则报错;
如果我们想模拟封装一个这样的any
呢?
- 声明 count 变量用来统计失败的次数,如果全部失败,则 reject err:
- 对于执行成功的 Promise 函数,和
race
一样使用抢占式机制,直接返回,先到先得;
any
的封装代码:
const any = (promiseList) => {
let count = 0;
return new MyPromise((resolve, reject) => {
promiseList.forEach((promise) => {
promise.then(res => {
resolve(res)
}, err => {
count++;
if (count === promiseList.length) {
reject('All promises are rejected')
}
})
})
})
}
三.Promise A+规范
上文我们实现了简版的 Promise,接下来看下标准的 Promise/A+ 的规范:
对照的翻译如下:
一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。
一个 promise 表示异步操作的最终结果。与 promise 进行交互的主要方式是通过它的方法 then。该方法通过注册回调来得到这个 promise 的最终 value ,或者为什么这个 promise 不能被 fulfilled 的 reason 。
该规范详细说明了 then 方法的行为,提供了一个可互操作的基础,因此所有符合 Promises/A+
的 promise 实现都可以依赖该基础。尽管 Promises/A+
组织可能偶尔会通过向后兼容的微小更改来修改此规范,以解决新发现的情况,但我们只有在仔细考虑、讨论和测试后才会进行大的或向后不兼容的更改。因此, 该规范应该被认为是十分稳定的 。
从历史上看, Promises/A+ 阐明了早期 Promises/A proposal 的条款,并将部分事实上已经实现的拓展涵盖其中,以及对某些未指定或者有问题的部分省略。
最后,Promises/A+
规范的核心不包括:如何 create 、fulfill 或 reject promises,而是选择专注于提供可互操作的 then 方法。不过伴随规范的未来工作可能会涉及这些主题。
这里可以看到,Promises/A+
规范目前的核心是规范 then 方法,并没有对如何实现 promise 以及如何改变 promise 的状态进行限制。
1.术语
prmoise
是一个拥有符合本规范的 then 方法的对象或者函数;thenable
是一个定义了 then 方法的对象或者函数;value
是 JavaScript 的任意合法值(包括 undefined, thenable, promise);exception
是一个用 throw 语句抛出的 value ;reason
是一个表示 promise 被 rejected 的 value ;
2.要求
2.1 promise 的状态
pormise 必须是以下三个状态之一: pending, fulfilled, rejected。
-
当 promise 处于 pending 状态时:
- 可以转换到 fulfilled 或 rejected 状态;
-
当 promise 处于 fulfilled 状态时:
- 不能转换到其他状态;
- 必须有一个 value ,并且不能改变;
-
当 promise 处于 rejected 状态时:
- 不能转换到其他状态;
- 必须有 reason ,并且不能改变;
2.2 then方法
promise 必须提供一个 then 方法,能由此去访问当前或最终的 value 或者 reason 。
pormise 的 then 方法, 接受两个参数:
promise.then(onFulfilled, onRejected)
-
onFulfilled 和 onRejected 都是可选参数:
- 如果 onFulfilled 不是函数,则忽略;
- 如果 onRejected 不是函数,则忽略;
-
如果 onFulfilled 是一个函数:
- 它必须在 promise 被 fulfilled 后,以 promise 的 value 作为第一个参数调用;
- 它不能在 promise 被 fulfilled 之前调用;
- 它不能被调用多次;
-
如果 onRejected 是一个函数:
- 它必须在 promise 被 rejected 后,以 promise 的 reason 作为第一个参数调用;
- 它不能在 promise 被 rejected 之前调用;
- 它不能被调用多次;
- 在 execution context 栈(即 执行上下文栈)只包含平台代码之前, onFulfilled 或者 onRejected 不能被调用 (译者注: 异步执行回调);
-
onFulfilled 或者 onRejected 必须以函数形式调用(即不能有this值);
-
then 方法可以被同一个 promise 调用多次:
- 如果或者当 promise 处于 fulfilled 状态, 所有自己的 onFulfilled 回调函数,必须要按照 then 注册的顺序被调用;
- 如果或者当 promise 处于 rejected 状态, 所有自己的 onRejected 回调函数,必须要按照 then 注册的顺序被调用;
-
then 方法必须要返回 promise:
promise2 = promise1.then(onFulfilled, onRejected);
- 如果 onFulfilled 或者 onRejected 返回一个值 x ,则执行 Promise Resolution Procedure:
[[Resolve]](promise2, x)
; - 如果 onFulfilled 或者 onRejected 抛出异常 e , promise2 必须以 e 作为 reason ,转到 rejected 状态;
- 如果 onFulfilled 不是函数,并且 promise1 处于 fulfilled 状态 ,则 promise2 必须以与 promise1 同样的 value 被 fulfilled;
- 如果 onRejected 不是函数,并且 promise1 处于 rejected 状态 ,则 promise2 必须以与 promise1 同样的 reason 被 rejected;
2.3 Promise Resolution Procedure
Promise Resolution Procedure 是一个抽象操作。它以一个 promise 和一个 value 作为输入,记作:[[Resolve]](promise, x)
。 如果 x 是一个 thenable , 它会尝试让 promise 变成与 x 的一样状态 ,前提 x 是一个类似的 promise 对象。否则,它会让 promise 以 x 作为 value 转为 fulfilled 状态。
这种对 thenables 的处理允许不同的 promise 进行互操作,只要它们暴露一个符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法“同化”不一致的实现。
[[Resolve]](promise, x)
执行以下步骤:
-
如果 promise 和 x 引用的是同一个对象,则以一个 TypeError 作为 reason 让 promise 转为 rejeted 状态;
-
如果 x 也是一个 promise ,则让 promise 接受它的状态:
- 如果 x 处于 pending 状态,promise 必须保持 pending 状态,直到 x 变成 fulfilled 或者 rejected 状态,promise 才同步改变;
- 如果或者当 x 处于 fulfilled 状态, 以同样的 value 让 promise 也变成 fulfilled 状态;
- 如果或者当 x 处于 rejected 状态, 以同样的 reason 让 promise 也变成 rejected 状态;
-
如果 x 是一个对象或者函数:
-
令 then 等于 x.then;
-
如果读取 x.then 抛出异常 e , 以 e 作为 reason 让 promise 变成 rejected 状态;
-
如果 then 是一个函数,以 x 作为 this 调用它,传入第一个参数 resolvePromise , 第二个参数 rejectPromise :
-
如果 resolvePromise 被传入 y 调用, 则执行
[[Resolve]](promise, y)
; -
如果 rejectedPromise 被传入 r 调用,则用,r 作为 reason 让 promise 变成 rejected 状态;
-
如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用多次了。只有第一次调用生效,其余会被忽略;
-
如果调用 then 抛出异常 e:
- 如果 resolvepromise 或 rejectPromise 已经被调用过了,则忽略它;
- 否则, 以 e 作为 reason 让 promise 变成 rejected 状态;
-
如果 then 不是一个函数,以 x 作为 value 让 promise 变成 fulfilled 状态;
-
-
-
如果 x 不是对象或函数, 以 x 作为 value 让 promise 变成 fulfilled 状态;
如果一个 promise 被一个循环的 thenable 链中的对象 resolved,而 [[Resolve]](promise, thenable)
的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励实现者检测这样的递归是否存在,并且以 TypeError 作为 reason 拒绝 promise。
四.async/await
1. 介绍
ES2017 标准引入了async
函数,使得异步操作变得更加方便。
先说一下async
的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async
就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。
写一个async 函数:
async function fn() {
console.log('hello world')
}
语法很简单,就是在函数前面加上async
关键字,来表示它是异步的,那怎么调用呢?async 函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async
函数调用之后加一句console.log;
async function fn() {
console.log('hello world')
}
fn()
console.log(11111);
async
关键字差不多了,我们再来考虑await
关键字,await
是等待的意思,那么它等待什么呢?它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多的是放一个返回promise
对象的表达式。
注意:await
关键字只能放到async
函数里面。
现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2:
function request(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * 2)
}, 2000)
})
}
现在再写一个async
函数,从而可以使用await
关键字, await
后面放置的就是返回 promise 对象的一个表达式,所以它后面可以写 request 函数的调用:
async function fn() {
let res = await request(100);
console.log(res);
}
fn() // 2s之后输出 200
现在我们看看代码的执行过程,调用 fn 函数,它里面遇到了await
, await
表示等一下,代码就暂停到这里,不再向下执行了,它等什么呢?等后面的 promise 对象执行完毕,然后拿到 promise resolve 的值并进行返回,返回值拿到之后,它继续向下执行。
就这一个函数,我们可能看不出 async/await 的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?
async function fn() {
let res1 = await request(100);
let res2 = await request(200);
let res3 = await request(300);
console.log(res1 + res2 + res3);
}
fn(); // 6s之后输出 1200
至此,我们可以看到,写异步代码就像写同步代码一样了,真的方便多了。
2. 详解
从上面的讲义中,我们知道了async/await的用处:用同步方式,执行异步操作。
我们用 request 函数模拟接口请求:
function request(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num * 2)
}, 1000)
})
}
现在有一个需求:先请求完接口1,再拿接口1返回的数据,去当做接口2的请求参数;再拿接口2返回的数据,去当做接口3的请求参数;
request(1).then(res1 => {
// 2s之后先打印2
console.log(res1);
request(res1).then(res2 => {
// 4s之后打印4
console.log(res2);
request(res2).then(res3 => {
// 6s之后打印8
console.log(res3);
})
})
})
如果嵌套的多了,这个时候就可以用async/await来解决了:
async function fn() {
const res1 = await request(1);
console.log(res1);
const res2 = await request(res1);
console.log(res2);
const res3 = await request(res2)
console.log(res3);
}
fn();
在async
函数中,await
规定了异步操作只能一个一个排队执行,从而达到用同步方式,执行异步操作的效果。
注意:await只能在async函数中使用
刚刚上面的例子await
后面都是跟着异步操作 Promise,那如果不接 Promise呢?
function request(num) {
// 去掉Promise
setTimeout(() => {
console.log(num * 2)
}, 1000)
}
async function fn() {
await request(1) // 2
await request(2) // 4
// 1秒后执行完 同时输出
}
fn()
可以看出,如果await
后面接的不是 Promise 的话,其实是达不到类似同步的效果的。
什么是async
?
async
是一个位于 function 之前的前缀,只有async
函数中,才能使用await
。那async
执行完是返回是什么?
async function fn() {}
console.log(fn) // [AsyncFunction: fn]
console.log(fn()) // Promise {<fulfilled>: undefined}
可以看出,async
函数执行完会自动返回一个状态为 fulfilled 的 Promise,也就是成功状态,但是值却是undefined,那要怎么才能使值不是 undefined 呢?只要函数有 return 返回值就行了。
async function fn(num) {
return num
}
console.log(fn) // [AsyncFunction: fn]
console.log(fn(10)) // Promise {<fulfilled>: 10}
fn(10).then(res => console.log(res)) // 10
3.总结
await
只能在async
函数中使用,不然会报错;async
函数返回的是一个 Promise 对象,有无值看有无 return 值;await
后面最好是接 Promise,如果await
后面是 promise 对象会造成异步函数停止执行并且等待 promise 的解决,如果await
后面是正常的表达式则立即执行;async/await
作用是用同步方式,执行异步操作;
4.语法糖
Q:async/await
是一种语法糖,那么什么是语法糖呢?
A:语法糖是简化代码的一种方式,用其他方式也能达到同样的效果,但写法可能没有这么便利。套了一层外衣
,ES6的class
也是语法糖,因为其实用普通 function 也能实现声明构造函数的同样效果。
回归正题,async/await
是一种语法糖,用到的是ES6里的迭代函数——generator函数。
五.generator
1.介绍
generator
函数跟普通函数在写法上的区别就是,多了一个星号*
,并且只有在generator
函数中才能使用yield
,而yield
相当于generator
函数执行的中途暂停点。比如下方有3个暂停点,而怎么才能暂停后继续走呢?那就得使用到next
方法,next
方法执行后会返回一个对象,对象中有value
和done
两个属性:
- value:暂停点后面接的值,也就是yield后面接的值;
- done:generator函数是否已走完,未走完为false,走完则为true;
// 使用 * 声明迭代器
function* gen() {
yield 1
yield 2
yield 3
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
可以看到最后一个执行结果的 value 是 undefined,这取决于声明的generator
函数有无返回值。
我们对上面的例子稍加调整,return一个返回值,再来看下执行结果:
function* gen() {
yield 1
yield 2
yield 3
return 4
}
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: 4, done: true }
可以看到,现在最后一个执行结果的value就是我们return的值。
1.1 yield后接函数
yield
后面接函数的话,到了对应暂停点yield
,会马上执行此函数,并且该函数的执行返回值,会被当做此暂停点对象的value
// yield后面的函数
function fn(num) {
console.log(num)
return num
}
function* gen() {
yield fn(1)
yield fn(2)
return 3
}
const g = gen()
console.log(g.next())
// 1
// { value: 1, done: false }
console.log(g.next())
// 2
// { value: 2, done: false }
console.log(g.next())
// { value: 3, done: true }
1.2 yield后接promise
我们再来看一下yield
后面如果接的是 promise 函数,会有怎样的效果:
// yield后面的romise
function fn(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num)
}, 1000)
})
}
function* gen() {
yield fn(1)
yield fn(2)
return 3
}
const g = gen()
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: 3, done: true }
如果想获取的是两个 Promise 的结果1和2,可以使用 Promise 的then:
const g = gen()
const next1 = g.next()
next1.value.then(res1 => {
console.log(next1) // 1秒后输出 { value: Promise { 1 }, done: false }
console.log(res1) // 1秒后输出 1
const next2 = g.next()
next2.value.then(res2 => {
console.log(next2) // 2秒后输出 { value: Promise { 2 }, done: false }
console.log(res2) // 2秒后输出 2
const next3 = g.next()
console.log(next3) // 2秒后输出 { value: 3, done: true }
})
})
1.3 next函数传参
generator
函数可以用next
方法来传参,并且可以通过yield
来接收这个参数。
但是需要注意两点:
- 第一次
next
传参是没用的,只有从第二次开始next
传参才有用; next
传值时,要记住顺序是:先右边yield,后左边接收参数;
function* gen() {
const num1 = yield 1
console.log(num1)
const num2 = yield 2
console.log(num2)
return 3
}
const g = gen()
console.log(g.next(11111))
// { value: 1, done: false }
console.log(g.next(22222))
// 22222
// { value: 2, done: false }
console.log(g.next(33333))
// 33333
// { value: 3, done: true }
1.4 Promise&next传参
根据上文可以知道:
yield
后面可以接Promise;next
函数可以传参;
所以一起使用时的效果为:
function fn(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num * 2)
}, 1000)
})
}
function* gen() {
const num1 = yield fn(1)
const num2 = yield fn(num1)
const num3 = yield fn(num2)
return num3
}
const g = gen()
const next1 = g.next()
next1.value.then(res1 => {
console.log(next1) // 1秒后同时输出 { value: Promise { 2 }, done: false }
console.log(res1) // 1秒后同时输出 2
const next2 = g.next(res1) // 传入上次的res1
next2.value.then(res2 => {
console.log(next2) // 2秒后同时输出 { value: Promise { 4 }, done: false }
console.log(res2) // 2秒后同时输出 4
const next3 = g.next(res2) // 传入上次的res2
next3.value.then(res3 => {
console.log(next3) // 3秒后同时输出 { value: Promise { 8 }, done: false }
console.log(res3) // 3秒后同时输出 8
// 传入上次的res3
console.log(g.next(res3)) // 3秒后同时输出 { value: 8, done: true }
})
})
})
2.实现 async/await
上方的generator
函数的Promise&next传参,就很像async/await了,区别在于
- gen 函数执行返回值不是 Promise,asyncFn 执行返回值是 Promise;
- gen 函数需要执行相应的操作,才能等同于 asyncFn 的排队效果;
- gen 函数执行的操作是不完善的,因为并不确定有几个 yield,不确定会嵌套几层;
针对这种情况,可以通过高阶函数(HOC)封装:
function highorderFn(函数) {
// 一系列处理
return 函数
}
根据上述代码,可以封装一个高阶函数,接收一个generator
函数,并经过处理,返回一个具有async
函数功能的函数:
function generatorToAsync(generatorFn) {
// 经过一系列处理
return 具有async函数功能的函数
}
2.1 返回值promise
function* gen() {
}
const asyncFn = generatorToAsync(gen)
console.log(asyncFn()) // 这里期望输出 Promise
这里需要针对generatorToAsync
进行处理:
function* gen() {
}
function generatorToAsync (generatorFn) {
return function () {
return new Promise((resolve, reject) => {
})
}
}
const asyncFn = generatorToAsync(gen)
console.log(asyncFn()) // Promise
2.2 结合上述代码
把之前的处理代码,加入generatorToAsync函数中。
function fn(num) {
return new Promise(resolve => {
setTimeout(() => {
resolve(num * 2)
}, 1000)
})
}
function* gen() {
const num1 = yield fn(1)
const num2 = yield fn(num1)
const num3 = yield fn(num2)
return num3
}
function generatorToAsync(generatorFn) {
return function () {
return new Promise((resolve, reject) => {
const g = generatorFn()
const next1 = g.next()
next1.value.then(res1 => {
const next2 = g.next(res1) // 传入上次的res1
next2.value.then(res2 => {
const next3 = g.next(res2) // 传入上次的res2
next3.value.then(res3 => {
// 传入上次的res3
resolve(g.next(res3).value)
})
})
})
})
}
}
const asyncFn = generatorToAsync(gen)
asyncFn().then(res => console.log(res)) // 3秒后输出 8
到这里,就已经实现了async/await的初始功能了:
async function asyncFn() {
const num1 = await fn(1)
const num2 = await fn(num1)
const num3 = await fn(num2)
return num3
}
asyncFn().then(res => console.log(res)) // 3秒后输出 8
2.3 支持多个await方法
因为async中可以支持若干个await,await的个数是不确定的。同样类比,generator函数中,也可能有2个yield,3个yield,5个yield,所以需要对上述代码进行改造:
function generatorToAsync(generatorFn) {
return function () {
const gen = generatorFn.apply(this, arguments) // gen有可能传参
// 返回一个Promise
return new Promise((resolve, reject) => {
function go(key, arg) {
let res
try {
res = gen[key](arg) // 这里有可能会执行返回reject状态的Promise
} catch (error) {
return reject(error) // 报错的话会走catch,直接reject
}
// 解构获得value和done
const {value, done} = res
if (done) {
// 如果done为true,说明走完了,进行resolve(value)
return resolve(value)
} else {
// 如果done为false,说明没走完,还得继续走
// value有可能是:常量,Promise,Promise有可能是成功或者失败
return Promise.resolve(value).then(val => go('next', val), err => go('throw', err))
}
}
go("next") // 第一次执行
})
}
}
const asyncFn = generatorToAsync(gen)
asyncFn().then(res => console.log(res))
2.4 测试结果
- async/await:
async function asyncFn() {
const num1 = await fn(1)
console.log(num1) // 2
const num2 = await fn(num1)
console.log(num2) // 4
const num3 = await fn(num2)
console.log(num3) // 8
return num3
}
const asyncRes = asyncFn()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8
- generatorToAsync:
function* gen() {
const num1 = yield fn(1)
console.log(num1) // 2
const num2 = yield fn(num1)
console.log(num2) // 4
const num3 = yield fn(num2)
console.log(num3) // 8
return num3
}
const genToAsync = generatorToAsync(gen)
const asyncRes = genToAsync()
console.log(asyncRes) // Promise
asyncRes.then(res => console.log(res)) // 8
到这里,我们就基本完成了async/await的封装。
2.5 更多资料
原文链接:https://juejin.cn/post/7314519123177668620 作者:Nunumaymax