异步处理 —— Promise的辅助函数和async/await(二)

目标:

  • 掌握 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 方法返回一个对象,值为 valuedonevalueyield 后面的值,done 表示 generator 是否已经走完
  • yield 后面是函数的话,到了对应的 yield 会马上执行后面的函数,并将此函数的返回值作为该处 nextvalue
  • 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 的初始功能,还需要进行改造,asyncawait 的数量是不固定的,同理,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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值