FE-Interview icon indicating copy to clipboard operation
FE-Interview copied to clipboard

第 24 题:实现 Promise.all

Open lgwebdream opened this issue 4 years ago • 13 comments

Promise.all = function (arr) {
  // 实现代码
};

lgwebdream avatar Jun 19 '20 12:06 lgwebdream

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]
})

lgwebdream avatar Jun 19 '20 12:06 lgwebdream

  /**
   * 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)
      })
  })

}

sjw0608 avatar Jul 10 '20 12:07 sjw0608

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]
});

GolderBrother avatar Jul 21 '20 14:07 GolderBrother


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
});

songshuangfei avatar Sep 08 '20 11:09 songshuangfei

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/

keshidong avatar Jan 13 '21 06:01 keshidong

Promise.all = async (promises) => {
  const results = [];
  for (promise of promises) {
    results.push(await promise);
  }

  return results;
};

Fnll avatar Apr 11 '21 16:04 Fnll

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)
})

Luoyuda avatar Jun 10 '21 03:06 Luoyuda

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)
    })

Luoyuda avatar Jun 10 '21 03:06 Luoyuda

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]
})

zizxzy avatar Nov 05 '21 12:11 zizxzy

 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]
    })

SnailOwO avatar Nov 11 '21 07:11 SnailOwO

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); };

52mti avatar Mar 21 '22 13:03 52mti

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)
})

chinbor avatar Aug 02 '23 07:08 chinbor

/**
 * 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)
);

Kisthanny avatar Mar 21 '24 02:03 Kisthanny