目标:
- 掌握 Promise 的辅助函数的用法:all、race、allSettled、any
- 掌握 anync\await 的用法
- 了解 generator
- 实现async\await
1、辅助函数
注:以下所有方法传入的 Promise
数组中非 Promise
的项会当作成功来处理
1.1、all
let P1 = new Promise((re, rj) => {
re('p1')
})
let P2 = new Promise((re, rj) => {
re('p2')
})
let P3 = new Promise((re, rj) => {
re('p3')
})
Promise.all([P1,P2,P3,'p4']).then(res => {
console.log(res)
}, err => {console.log(err)})
// 输出 ['p1', 'p2', 'p3', 'p4']
特点:
- 如果所有的
Promise
都成功了,则返回所有的成功结果数组 - 如果至少有一个
Promise
失败了,返回第一个失败的结果 - 如果接收的数组中有非
Promise
对象,那么这项当作成功处理
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = (index, value) => {
result[index] = value
count++
if (count === promises.length) resolve(result)
}
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(index, res)
}, err => reject(err))
} else {
addData(index, promise)
}
})
})
}
1.2、race
let P1 = new Promise((re, rj) => {
setTimeout(() => {
re('p1')
}, 1000)
})
let P2 = new Promise((re, rj) => {
re('p2')
})
let P3 = new Promise((re, rj) => {
rj('p3')
})
Promise.race([P1,P2,P3]).then(res => {
console.log(res)
}, err => {console.log(err)})
// 输出'p2'
race
是竞速的意思,顾名思义,哪个 Promise
跑的最快就用哪个结果,无论它是成功还是失败
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}
1.3、allSettled
let P1 = new Promise((re, rj) => {
setTimeout(() => {
re('p1')
}, 1000)
})
let P2 = new Promise((re, rj) => {
rj('p2')
})
let P3 = new Promise((re, rj) => {
re('p3')
})
Promise.allSettled([P1,P2,P3, 'p4']).then(res => {
console.log(res)
}, err => {console.log(err)})
/* 输出:
0: {status: 'fulfilled', value: 'p1'}
1: {status: 'rejected', reason: 'p2'}
2: {status: 'fulfilled', value: 'p3'}
3: {status: 'fulfilled', value: 'p4'}
*/
执行完所有 Promise
之后按顺序把每一个的结果输出,非 Promise
的项当作成功处理
static allSettled(promises) {
return new Promise((resolve, reject) => {
const res = []
let count = 0
const addData = (status, value, i) => {
res[i] = {
status,
value
}
count++
if (count === promises.length) {
resolve(res)
}
}
promises.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData('fulfilled', res, i)
}, err => {
addData('rejected', err, i)
})
} else {
addData('fulfilled', promise, i)
}
})
})
}
1.4、any
let P1 = new Promise((re, rj) => {
setTimeout(() => {
rj('p1')
}, 1000)
})
let P2 = new Promise((re, rj) => {
rj('p2')
})
let P3 = new Promise((re, rj) => {
rj('p3')
})
Promise.any([P1,P2,P3]).then(res => {
console.log(res)
}, err => {console.log(err)})
// 输出:AggregateError: All promises were rejected
只要有一项 Promise
执行成功就返回该项的值,如果所有的 Promise
都失败的话,则报错。
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((promise) => {
promise.then(val => {
resolve(val)
}, err => {
count++
if (count === promises.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
}
2、async/await
async/await
的作用就是用同步的方式执行异步操作,在async函数中,await
规定了异步操作只能一个一个排队执行,从而达到用同步方式,执行异步操作的效果。
注:await
只能在 async
中使用,而且 await
后面必须跟着 Promise
request().then(res1 => {
request(res1).then(res2 => {
request(res2).then(res3 => { console.log(res3 })
})
})
像上面的 then
里面嵌套 then
,嵌套的多了会使代码看起来混乱,这时候就可以用 async/await
来解决这个问题了
async function fn() {
let res1 = await request()
let res2 = await request(res1)
let res3 = await request(res2)
return res3
}
fn().then(res => { console.log(res) })
async
函数返回值是 Promise
是对象;
async/await
其实是语法糖(语法糖是简化代码的一种方式,用其他方式也能达到同样的效果,但写法可能没有这么便利),它是通过 ES6
里的 generator
来实现的
3、generator
3.1、用法
generator
的用法如下:
- 与普通函数相比,加上
*
号表示是generator
yield
只有在generator
函数中才能使用,它相当于函数执行中的暂停点- 只有执行
next
方法才能继续执行,next
方法返回一个对象,值为value
和done
,value
为yield
后面的值,done
表示generator
是否已经走完 yield
后面是函数的话,到了对应的yield
会马上执行后面的函数,并将此函数的返回值作为该处next
的value
值next
可以传参,yield
可以接受参数。第一个next
传参是没有效果的,第二个开始传的参数赋给前一个yield
赋值的变量,因为next
参数只赋值给已经执行完毕的yield
语句左边的变量
function fun(num) {
return num
}
function P(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num)
}, 1000)
})
}
function* gen() {
yield 1
yield fun(2)
const temp = yield 3
console.log('temp', 333)
yield P(4)
return 5
}
const g = gen()
console.log(g.next()) // { value: 1, done: false } //value为yield后面的值,generator表示是否已经走完
console.log(g.next()) // { value: 2, done: false } //yield后面的值为函数的话,value为函数的返回值
const next = g.next(333) // 输出 'temp', 333;next方法可以传参,然后通过yield接收参数
console.log(next) //{ value: Promise { <pending> }, done: false }
next.value.then(res => {
console.log(res) // 1秒后输出4
console.log(g.next()) // 1秒后输出 { value: 5, done: true }
})
function* gen1() {
yield 1
yield 2
return 3
}
const g1 = gen1()
console.log(g1.next()) // { value: 1, done: false }
console.log(g1.next()) // { value: 2, done: false }
console.log(g1.next()) // { value: 3, done: true } // generator函数有返回值,那此处的value就为return的值,否则为undefined
function* gen2() {
yield fun(1)
yield fun(2)
}
const g2 = gen2()
console.log(g2.next()) // { value: 1, done: false }
console.log(g1.next()) // { value: 2, done: false }
console.log(g1.next()) // { value: undefined, done: true }
function* gen3() {
const num1 = yield 1
console.log('num1', num1)
const num2 = yield 2
console.log('num2', num2)
}
const g3 = gen3()
console.log(g3.next(111))
console.log(g3.next(222))
console.log(g3.next(333))
/*
{ value: 1, done: false }
num1 222
{ value: 2, done: false }
num2 333
{ value: undefined, done: true }
*/
function* gen4() {
yield P(1)
yield P(2)
}
const g4 = gen4()
const next1 = g4.next()
console.log(next1) // { value: Promise { <pending> }, done: false }
next1.value.then(res1 => {
console.log(res1) //1秒后输出1
const next2 = g.next()
console.log(next2) //1秒后输出 { value: Promise { <pending> }, done: false }
next2.value.then(res2 => {
console.log(res2) //2秒后输出2
})
})
3.2、实现 async\await
generator
需要满足以下特点才能达到 async/await
的效果
async
的返回值是Promise
对象,而genaretor
不是gen
函数里需要实现相应的操作,才能达到等同于async/await
的排队效果gen
函数的操作是不完善的,因为并不确定有几个yield
,不确定会嵌套几次
针对上述情况,可以通过封装高阶函数(HOC):
高阶函数:参数是函数,返回值也可以是函数。
function highorderFn(函数) { return 函数 }
function fn(nums) {
return new Promise(resolve => {
setTimeout(() => {
resolve(nums * 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
中 await
的数量是不固定的,同理,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))
3.3、测试结果
/* 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