为for循环而不是为每个创建承诺?

时间:2017-07-05 16:19:15

标签: javascript foreach promise parse-server for-of-loop

我想并行执行一个Promises数组,然后等到所有Promise都被执行。

这有效:

var promises = [];

objects.forEach(function(object) {

    let promise = new Parse.Promise.as()
    .then(
        function() {
            return destroy(object);
        }
    );

    promises.push(promise);
});

return Parse.Promise.when(promises);

但如果我使用for (object of objects) {...}代替objects.forEach(function(object) {...});,则无效。对于数组中的每个Promise,destroy(object);在数组中的第一个对象上执行:

var promises = [];

for (object of objects) {

    let promise = new Parse.Promise.as()
    .then(
        function() {
            return destroy(object);
        }
    );

    promises.push(promise);
});

return Parse.Promise.when(promises);

为什么?

2 个答案:

答案 0 :(得分:4)

是的,您忘了将object变量声明为循环体的局部变量(另请参阅canonical explanation):

var promises = [];
for (let object of objects) {
//   ^^^
    promises.push(new Parse.Promise.as().then(function() {
        return destroy(object);
    }));
}
return Parse.Promise.when(promises);

当然你不应该这样做,你应该只使用map

var promises = objects.map(function(object) {
    return new Parse.Promise.as().then(function() {
        return destroy(object);
    });
});
return Parse.Promise.when(promises);

答案 1 :(得分:0)

修改 我最初有点困惑,复制&粘贴我写的松弛的东西。对于那个很抱歉。

就像@Bergi所说,如果你需要一个objects数组的Promises数组,那么通常使用.map()转换现有数组会更好。如上所述,这看起来像:

const getPromises = (objects) => {
  return objects.map(object => new Parse.Promise.as()
    .then(() => {
      return destroy(object);
    })
  );
}
// No 'temp' array needed, and most important: promise variables aren't 'lost'

// Then you can similarly use this pattern:
return Parse.Promise.when(getPromises(objects));
// Or this:
return Promise.all(getPromises(objects));

我原来的答案(下面)本身有点含糊不清,希望我上面的答案给出了更多的背景。 :)

我避免使用for循环或Array.forEach。当我发现.forEach()我将它用于一切时,假设它是我for loops混乱的答案。我来学习两者都是99.99%的代码味道。原因:它们通常需要临时数组,并使嵌套循环非常笨拙。

虽然.map()在数组大小相同(1:1或20:20)时很大程度上解决了这个问题,但其他Array方法对于非对称变换非常有用,20:1(例如:总计成本)将20个产品分成1个数字,或找到列表中最大的交易):

.map    - use for 1:1 array transforms, as @bergi suggests.
.reduce - useful for transforming 1 array into ANYTHING else. Need a sum or subtotal? Or results grouped by day? Use .reduce().
.filter - return only items which result in a `true` result
.find   - use to avoid full array scans when only 1 item must be returned. 
.some   - exit array scan returning true at first opportunity

let downloadedMsgs  = emails.map(m => downloadBody(m))
let recipientsCount = emails.reduce((count, m) => count + m.to.length, 0)
let onlyRecentMsgs  = emails.filter(m => m.isNew)
let aRecentMsg      = emails.find(m => m.isNew)
let hasNewMessage   = emails.some(m => m.isNew)
// (Notice last 3 identical predicate fn's with different uses: aka pluripotency)

代码中的另一个问题是丢失Promise的风险!这很重要,如果您的阵列中可能有数十个或100个对象,则在循环中触发HTTP请求将非常不可靠(最终您将耗尽可用的套接字)。限制这一点的最好方法是返回你的Promises。