promise.all的javascript实现不起作用?

时间:2016-06-21 18:48:52

标签: javascript promise

我正在通过雄辩的javascript并且必须实现promise.all。这是我的解决方案。

function all(promises) {
  return new Promise(function(success, fail) {
    var results = [];
    var failed = false;
    promises.forEach(function(promise) { 
      promise.then(function(result) {
        results.push(result);
      }, function (error) {
        failed = true;
        fail(error);
      });
    });
    if (!failed)
      success(results);
  });
}

以下是我正在进行的测试。

// Test code.
all([]).then(function(array) {
  console.log("This should be []:", array);
});
function soon(val) {
  return new Promise(function(success) {
    setTimeout(function() { success(val); },
               Math.random() * 500);
  });
}
all([soon(1), soon(2), soon(3)]).then(function(array) {
  console.log("This should be [1, 2, 3]:", array);
});
function fail() {
  return new Promise(function(success, fail) {
    fail(new Error("boom"));
  });
 }
all([soon(1), fail(), soon(3)]).then(function(array) {
  console.log("We should not get here");
}, function(error) {
  if (error.message != "boom")
    console.log("Unexpected failure:", error);
});

我的代码显然是错误的,因为它的输出

这应该是[]:[]

这应该是[1,2,3]:[]

我们不应该到这里

第一个是唯一正确的。 实际的解决方案来自我的有缺陷的视图与我写的工作基本相同,可以在这里找到: http://eloquentjavascript.net/code/#17.2

为什么我的代码不起作用?它有什么问题?

3 个答案:

答案 0 :(得分:1)

forEach是同步的,if (!failed)语句将在Promise解析之前运行。您可以执行以下操作:

function all(promises) {
  var results = [],
      left = promises.length;
  if (!left) {
    return Promise.resolve(results)
  }
  return new Promise((res, rej) => {
    promises.forEach((p, i) => {
      Promise.resolve(p).then(x => {
        results[i] = x
        left -= 1
        if (left === 0) {
          res(results)
        }
      }, rej)
    })
  })
}

使用稀疏数组可以保持顺序,例如:

function timeout(n) {
  return new Promise((res) => {
    setTimeout(() => {
      res(n)
    }, n)
  })
}

var pa = timeout(200)
var pb = timeout(300)
var pc = timeout(100)

// success
all([pa, pb, pc]).then(console.log).catch(console.log) //=> [200, 300, 100]


// error
var pd = Promise.reject('error')

all([pa, pb, pc, pd]).then(console.log).catch(console.log) //=> error

答案 1 :(得分:0)

你到了

 if (!failed)                 
   success(results);

这么快。如果所有承诺都完成并且它是最后一个承诺,那么所有承诺都会得到解决。

function all(promises) {
    return new Promise(function(success, fail) {
        var results = [];
        var failed = false;
        promises.forEach(function(promise) {
            promise.then(function(result) {

                results.push(result);

                if (results.length == promises.length) {
                    if (!failed)
                        success(results);
                }
            }, function (error) {

                failed = true;
                fail(error);
            });
        });


    });
}

测试你的功能:

all([soon(1), fail(), soon(3)]).then(function(array) {
    console.log("We should not get here");
}, function(error) {
    console.log("Fail");
    if (error.message != "boom")
        console.log("Unexpected failure:", error);
});

但是,它仍然缺乏结果排序。

答案 2 :(得分:0)

正如其他人所说,即使在您的承诺得到解决之前,您的.forEach也会立即完成。对于.forEach中的每个承诺,异步计算需要一段随机的时间才能完成。对于此exercise,时间为Math.random() * 5000

因此,在每次迭代中,最终结果都是“承诺”,.forEach继续。这就是你得到一个空数组的原因:[].forEach已在您的任何承诺解决之前完成。

您要做的是跟踪解决的每个承诺,一旦您有三个已解决的承诺,您就可以在success阵列上调用results处理程序。您可以通过将处理程序之外的闭包变量保存到每个promise的.then()中来实现此目的。当每个promise完成时,您可以增加该变量。在我的解决方案中,我将此变量命名为count

function all(promises) {
  return new Promise(function(success, fail) {
    if (promises.length === 0) {
      return success([]);
    }
    const results = [];
    let count = 0;

    for (let i = 0; i < promises.length; i++) {
      promises[i].then(val => {
        results[i] = val;
        count++;

        if (count === promises.length) {
          return success(results);
        }
      }).catch(err => fail(err));
    }
  });
}