实现promise.all 但是效果并不如期望那样

知行合一
  • 9
 var makePromise = function(value, time) {
    return new Promise(function(resolve, reject){
      setTimeout(function() {
        resolve(value);
      }, time)
    })
  };
  
 function order1(promiseList) {
    return new Promise((resolve, reject) => {
        let result = [];
        let i = 0;
        for (let p in promiseList) {
            promiseList[p].then(value => {
                result[p] = value
                i++;
                console.log(value);
                if (i === promiseList.length) {
                    resolve(result);
                }
                return Promise.resolve(value)
            }, reject);
        }
      });
 }
 order1([
    makePromise('a', 3000),
    makePromise('b', 1000),
    makePromise('c', 2000),
  ]).then(v=>{
      console.log(v);
  })

在for循环的console.log中 data返回的是b,c,a 并不如期望的a,b,c 有大神指导下如何修改吗 这是一道面试题 使用递归的方法在then中打印data 是 延迟之后同时输出abc 在循环中则是每间隔1秒 输出bca

回复
阅读 608
3 个回答

用同步循环的话顺序不会变,各 Promise 之间没有影响,要强制把他们的resolve顺序改变,比较直观的方法是用递归:

function order1(orders){
  const values = [];
  return new Promise((resolve, reject) => {
    const handler = () => {
      const next = orders.shift();
      
      // 数组空了,说明处理完毕,直接 resolve
      if(!next) return resolve(values);

      next.then(value => {
        // 注意 Promise.all 会用数组包含全部的值
        values.push(value);
        console.log(value);
        
        // 递归处理
        handler();
      })
      // 出错就抛,并中断递归
      .catch(reject);
    };
    
    handler();
  })
}

同步循环(指没有 await 的循环)也可以做到与 Promise.all 规范一致:

function order1(orders){
  let counter = 0;
  const { length } = orders;
  
  const amILast = () => {
    return ++counter === length;
  }
  
  return new Promise((resolve, reject) => {
    const values = [];
    let anyError = false;
    let resolved = 0;
    for(const index in orders){
      const promise = orders[index];
      promise.then(value => {
        if(anyError) return;
        values[index] = value;
        
        if(++resolved === length){
          resolve(values)
        }
      }).catch(err => {
        anyError = true;
        reject(err);
      })
    }
  })
}

但是如果在 .then 中打印的话,打印顺序和他们在数组中的顺序是不一致的。

const sleep = (sign, timeout) => {
                return new Promise(resolve => {
                    setTimeout(resolve, timeout * 1000, sign);
                })
            }
            Promise.all = function(promiseList) {
                return new Promise((resolve, reject) => {
                    const result = [];
                    let count = 0;
                    for(let i = 0; i < promiseList.length; i++) {
                        promiseList[i]
                            .then(data => {
                                result[i] = data;
                                count++;
                                if (count === promiseList.length) {
                                    resolve(result);
                                }
                            })
                            .catch(e => {
                                reject(e);
                            })
                    }
                })
            }

            Promise.all([sleep(1, 1), sleep(2, 0.5), sleep(3, 0.2)])
                .then(console.log)

makePromise 的第二个参数是延迟时长吗?如果是的话那么 Promise 完成的顺序确实是b、c、a,thenconsole.log 依次输出 b、c、a 是正常现象。
Promise.all 需要保证的是全部完成后输出的结果保持原有顺序。

function makePromise(str, timing)  {
 return new Promise((resolve, reject) => {
    setTimeout(() => {resolve(str)}, timing);
 })
}
Promise.all([
    makePromise('a', 3000).then(data => {console.log(data);return data}),
    makePromise('b', 1000).then(data => {console.log(data);return data}),
    makePromise('c', 2000).then(data => {console.log(data);return data}),
  ]).then(v=>{
      console.log(v);
  })
 // 输出 b c a ["a", "b", "c"]
你知道吗?

宣传栏