前言
在Promise这个es6新增的对象身上有很多神奇的方法,通常被我们用来处理一些异步代码,比如里面的.then(),.catch()等,除了它的实例对象上面的方法,它本身也包括了一些静态方法可以让我们处理异步任务,接下来我们就来聊聊Promise.all()
Promise.all()简介
在我们开发的时候,我们有时候需要处理一堆的异步任务,并且需要获取到它们的结果,这时候有同学可能会想到用async/await来实现,这样虽然能实现,但是还是不够优雅,我们要写很多的await。这时我们就可以使用Promise.all ,它为我们提供了一种优雅的方式来同时执行这些操作,并等待它们全部完成。
作用效果:同时处理多个异步任务,并且返回一个存放它们结果的数组。(如果当中有失败的则会直接返回失败的结果)
Promise.all(params)的参数
参数类型
当我们看到()时,想必大家都知道这东西是个函数,而函数就离不开传参,Promise.all()这个方法的参数有点特别。我们自己定义的函数的参数通常是不会去规定类型的,而Promise.all()这个方法的参数类型是固定了的,上面的官方文档中说明了得是一个可迭代对象,接下来我们往里面分别传入一个set和{}试试:
我们通过浏览器控制台的打印可以看到,传入set不报错,而传入{}就报错了,并且报错的原因是object is not iterable。在这里我们就可以知道Promise.all()方法接收的参数类型是有规定的,必须接收个有迭代器属性的参数,否则就会报错。
迭代器属性是什么?
那什么是迭代器属性呢?我们来看看数组身上的一些方法:
这个属性就是我们所说的迭代器属性,它是一个函数,如果调用的话会返回一个迭代器。
Promise.all(params)的使用
在经过简单了解它传入参数的类型之后,接下来我们就可以学习它的使用了,上文说到它接收的参数得是一个可迭代的,我们接下来就以传入参数是数组为例子,数组中的每一项可以是同步也可以是异步,同样的我们也可以直接传入一个空数组,如果传入个空数组它会返回一个成功状态的Promise,并且值就是传入的空数组:
除了这个特点之外它还有两个非常重要的特点:
- 如果数组中所有的任务执行完都是fulfilled状态的Promise的话,就直接返回存放每个任务结果的数组,并且顺序和放置时是一样的
- 如果执行过程中出现了rejected状态的Promise则立即返回这个失败的结果。
手搓Promise.all()
相信各位在面试的时候或多或少都被手搓狠狠的折磨过,接下来我们来聊聊面试的经典题:手搓Promise.all(),在上文中简单了解了Promise.all的一些特性之后,我们可以总结出这个函数手搓主要实现的几个功能,接下来手搓我们以传入数组为例子:
- 判断参数类型,必须传入个迭代器。并且返回个Promise。
- 传入的是个空数组的话直接返回成功状态的空数组。
- 执行传入数组中的同步和异步任务,如果有rejected就直接返回失败原因,否则返回存放执行结果的数组。
- 返回数组结果必须跟传入参数时放置的位置一样
-
我们需要判断传入的参数类型,而这一点非常简单,我们只需要使用
for of来遍历执行即可实现,因为它只会遍历迭代器,不是迭代器它会直接报错。 -
我们需要将每一项通过
Promise.resolve()进行包装确保一致性处理 -
Promise.resolve()效果类似于返回一个成功状态的Promise,效果类似于下面。
接下来我们直接来看代码实现:
Promise.myAll = function (params) {
let res, rej // 储存resolve,reject
const p = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
let i = 0
const result = [] // 存放返回结果的数组
for (const item of params) { // 判断是不是传入的迭代器
const index = i
i++
Promise.resolve(item).then(data => { // 将每一项进行包装实现一致性
result[index] = data // 将对应结果放入对应位置
i--
if (i === 0) res(result) // 所有结果都是成功,就resolve出结果数组
}, rej)
}
if (i === 0) {
res([]) // 传入的空的数组,res出去
}
return p
}
Promise.myAll([1, 2, Promise.reject(1)])
.then(res => {
console.log(res);
})
.catch(err => console.log(err))
当然了,除了上面这种比较容易理解的写法,还有另外一种比较精简的写法:
Promise.myAll = function (promises) {
let result = [], count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
result[i] = res
count += 1
if (count === promises.length) resolve(result)
}, reject)
})
})
}
Promise.myAll([1, 2, 3, A(), Promise.resolve(9)]).then(
res => {
console.log('成功', res);
},
rej => {
console.log('失败', rej);
}
)