FE-Interview
FE-Interview copied to clipboard
第 24 题:实现 Promise.all
Promise.all = function (arr) {
// 实现代码
};
1) 核心思路
- ①接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数
- ②这个方法返回一个新的 promise 对象,
- ③遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
- ④参数所有回调成功才是成功,返回值数组与参数顺序一致
- ⑤参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。
2)实现代码 一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,如果其中一个接口失败了,多个请求也就失败了,页面可能啥也出不来,这就看当前页面的耦合程度了~
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if(!Array.isArray(promises)){
throw new TypeError(`argument must be a array`)
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedResult = [];
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promises[i]).then(value=>{
resolvedCounter++;
resolvedResult[i] = value;
if (resolvedCounter == promiseNum) {
return resolve(resolvedResult)
}
},error=>{
return reject(error)
})
}
})
}
// test
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
})
/**
* Promise.all
* @description 当这个数组里的所有promise对象全部变为resolve状态的时候,才会resolve, 当有一个promise对象变为reject状态时,就不再执行直接 reject
* @param {*} values promise对象组成的数组作为参数
*/
Promise.prototype.all = (values)=>{
return new Promise((resolve,reject)=>{
let resultArr = []
let count = 0
let resultByKey = (value,index)=>{
resultArr[index] = value
if(++count === values.length){
resolve(resultArr)
}
}
values.forEach((promise,index)=>{
promise.then((value)=>{
resultByKey(value,index)
},reject)
})
})
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new Error('argument must be a array');
}
// 用来记录Promise成功的次数
let resolveCount = 0,
// 用来保存Promise成功的结果
resolveDataList = [];
for (let index = 0, len = promises.length; index < len; index++) {
const p = promises[index];
Promise.resolve(p).then(data => {
resolveDataList[index] = data;
// promise成功次数等于promises数组长度,则成功
if (++resolveCount === len) {
resolve(resolveDataList);
}
// 有一个失败就失败
}, reject);
}
});
}
Promise.race = (promises = []) => {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
throw new TypeError(`argument must be a array`);
}
for (const p of promises) {
// 有一个成功就返回成功状态的promise
// 有一个失败就返回失败状态的promise
p.then(resolve, reject);
}
});
}
// Promise.finally() 最终的,无论如何finally中传递的回调函数 必须会执行,如果返回一个promise,会等待这个Promise执行完成
Promise.prototype.finally = function(callback){
return this.then(res => {
// 如果then方法返回一个Promise, 就会等待这个方法执行完毕,所以需要包装成Promise才能等待
return Promise.resolve(callback()).then(() => res);
}, err => {
return Promise.resolve(callback()).then(() => {
throw err;
})
});
}
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
Promise.all([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
});
Promise.myAll = function (arr) {
const length = arr.length;
let result = new Array(length);
let count = 0;
let succeedCount = 0;
let resolveAllFn = null;
let rejectAllFn = null;
let someErr = null;
function addcount() {
count++;
if (count === length) {
succeedCount === length
? resolveAllFn(result)
: rejectAllFn(someErr)
}
}
for (const [i, p] of arr.entries()) {
Promise.resolve(p).then((res) => {
result[i] = res;
succeedCount++;
addcount();
}, (err) => {
someErr = err;
addcount();
})
}
return new Promise((rs, rj) => {
resolveAllFn = rs;
rejectAllFn = rj;
})
}
Promise.myAll([
new Promise(rs=>{
setTimeout(() => {
rs(0)
}, 1000);
}),
Promise.resolve(1),
]).then((res) => {
console.log(res);//[ 0, 1]
});
Promise.myAll([
new Promise((_, rj)=>{
setTimeout(() => {
rj(0)
}, 1000);
}),
Promise.resolve(1),
]).then(() => {},err=>{
console.log(err); //0
});
Promise.all = (...promises) => {
const results = [];
const merged = promises.reduce(
(acc, p) => acc.then(() => p).then(r => results.push(r)),
Promise.resolve(null));
return merged.then(() => results);
};
from: https://eddmann.com/posts/implementing-promise-all-and-promise-race-in-javascript/
Promise.all = async (promises) => {
const results = [];
for (promise of promises) {
results.push(await promise);
}
return results;
};
function all(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve([])
let count = 0
let length = promiseList.length
let result = []
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
result[count++] = value
if(count === length) resolve(result)
}, reason => {
reject(reason)
})
}
})
}
function race(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve()
let length = promiseList.length
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
})
}
function allSettled(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve([])
let count = 0
let length = promiseList.length
let result = []
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
result[count++] = {
value,
status: 'fulfilled'
}
if(count === length) resolve(result)
}, reason => {
result[count++] = {
reason,
status: 'rejected'
}
if(count === length) resolve(result)
})
}
})
}
var promiseList = new Array(3).fill(0).map((item, index) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index)
}, index * 1000)
})
})
Promise.all(promiseList).then(list => {
console.log(list)
})
all(promiseList).then(list => {
console.log(list)
})
Promise.race(promiseList).then(list => {
console.log(list)
})
race(promiseList).then(value => {
console.log(value)
})
Promise.allSettled(promiseList).then(list => {
console.log(list)
})
allSettled(promiseList).then(list => {
console.log(list)
})
function all(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve([])
let count = 0
let length = promiseList.length
let result = []
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
result[count++] = value
if(count === length) resolve(result)
}, reason => {
reject(reason)
})
}
})
}
function race(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve()
let length = promiseList.length
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
})
}
function allSettled(promiseList) {
return new Promise((resolve, reject) => {
if(!promiseList || !promiseList.length) return resolve([])
let count = 0
let length = promiseList.length
let result = []
for (let i = 0; i < length; i++) {
Promise.resolve(promiseList[i]).then(value => {
result[count++] = {
value,
status: 'fulfilled'
}
if(count === length) resolve(result)
}, reason => {
result[count++] = {
reason,
status: 'rejected'
}
if(count === length) resolve(result)
})
}
})
}
var promiseList = new Array(3).fill(0).map((item, index) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(index)
}, index * 1000)
})
})
Promise.all(promiseList).then(list => {
console.log(list)
})
all(promiseList).then(list => {
console.log(list)
})
Promise.race(promiseList).then(list => {
console.log(list)
})
race(promiseList).then(value => {
console.log(value)
})
Promise.allSettled(promiseList).then(list => {
console.log(list)
})
allSettled(promiseList).then(list => {
console.log(list)
})
const promiseAll = (promises) => {
const newPromise = new Promise(function (resolve, reject) {
if (!Array.isArray(promises)) reject(new Error('promises is not array'));
const promiseLength = promises.length;
let promiseResolveCount = 0;
let promiseResolveResult = [];
for (let promise in promises) {
Promise.resolve(promise).then(value => {
promiseResolveResult.push(value);
promiseResolveCount++;
if (promiseResolveCount === promiseLength) resolve(promiseResolveResult);
}, error => {
reject(error);
})
}
});
return newPromise;
}
// test
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
})
async function promiseAll(ary = []) {
if (typeof ary[Symbol.iterator] !== 'function') {
throw new Error('尚未拥有Iterator接口')
}
const returnAry = []
try {
for (const val of ary) {
returnAry.push(await val)
}
return returnAry
} catch (err) {
console.log('all error:', err)
return err
}
}
let p1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(1)
}, 1000)
})
let p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
// resolve(2)
reject('demo error test')
}, 2000)
})
let p3 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res) // [3, 1, 2]
})
Promise.all = promises => { const executor = (resolve, reject) => { const responses = []; const success = res => { responses.push(res); if (responses.length === promises.length) resolve(responses); };
promises.forEach(promise => promise.then(success, reject));
}; return new Promise(executor); };
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let len = promises.length
const result = []
promises.forEach((promise, i) => {
Promise.resolve(promise).then((res) => {
len--
result[i] = res
if (len === 0) resolve(result)
}).catch(err => {
reject(err)
})
})
})
}
const promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 100)
})
Promise.all([promise1, promise2]).then(res => {
console.log(res)
}).catch(err => {
console.error(err)
})
/**
* Promise.all
* 接收一个promise列表
* 返回一个新的Promise对象
* 当所有promise都resolved时,按入参顺序resolve出value列表
* 如果有promise rejected,立刻reject该error
*/
上次手写的MyPromise
class MyPromise {
constructor(executor) {
this.status = "pending";
this.value = undefined;
this.error = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (data) => {
if (this.status === "pending") {
this.status = "fulfilled";
this.value = data;
this.onFulfilledCallbacks.forEach((cb) => cb(this.value));
}
};
const reject = (error) => {
if (this.status === "pending") {
this.status = "rejected";
this.error = error;
this.onRejectedCallbacks.forEach((cb) => cb(this.error));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.status === "fulfilled") {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
} else if (this.status === "rejected") {
try {
const result = onRejected(this.error);
resolve(result);
} catch (error) {
reject(error);
}
} else {
this.onFulfilledCallbacks.push((value) => {
try {
const result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push((error) => {
try {
const result = onRejected(error);
resolve(result);
} catch (err) {
reject(err);
}
});
}
});
}
}
all方法
MyPromise.all = function (promiseList) {
return new MyPromise((resolve, reject) => {
const valueList = promiseList.map(() => undefined);
let successCount = 0;
promiseList.forEach((p, index) => {
p.then(
(res) => {
valueList[index] = res;
successCount++;
if (successCount.length === promiseList.length) {
resolve(valueList);
}
},
(err) => {
reject(err);
}
);
});
});
};
测试
const p1 = new MyPromise((res, rej) => {
setTimeout(() => {
res(1);
}, 5000);
});
const p2 = new MyPromise((res, rej) => {
setTimeout(() => {
res(2);
}, 6000);
});
const p3 = new MyPromise((res, rej) => {
setTimeout(() => {
rej(new Error(3));
}, 1500);
});
MyPromise.all([p1, p2, p3]).then(
(res) => console.log(res),
(err) => console.error(err)
);