【问题标题】:Break for loop inside then() of a promise在 promise 的 then() 中中断 for 循环
【发布时间】:2016-01-28 16:28:35
【问题描述】:

我遇到了一种奇怪的情况,我想在收到 Rx Promise 的结果并进行一些检查后打破 for 循环。我有以下内容:

function getDrift(groups) {
    var drift = {};
    groups.forEach(function(group) {
        if(group.type === 'something') {
            for(var i = 0; i < group.entries.length; i++) {
                fetchEntry(group.entries[i].id)
                 .then(function(entry) {
                     if(entry.type === 'someType'){
                         drift[entry._id] = getCoordinates(entry);
                         // break;
                     }
                 });
            }
        }
    });
    return drift;
}

fetchEntry 正在返回基于 id 的 mongodb 文档的 Promise。如果满足if 检查,我想打破当前group.entries 的循环并继续下一组。

这可能吗?

谢谢

编辑:根据要求,组对象如下所示:

[
    {
        type: 'writing',
        entries: [{id: "someId", name: "someName"}, {id: "someId2", name: "someName2"}]
    },
    {
        type: 'reading',
        entries: [{id: "someId3", name: "someName3"}, {id: "someId4", name: "someName4"}]
    }
]

解决方案:我最终使用@MikeC 的建议,使用递归和回调来返回所需的值。谢谢大家!

【问题讨论】:

  • 您在这里遇到的问题是请求(可能)是异步的,而循环是同步的。因此,前者不能破坏后者,因为当回调触发时,循环早已结束。

标签: javascript promise rxjs


【解决方案1】:

这是不可能的,因为 Promise 是异步的,这意味着 then won't execute until all other synchronous code completes.

如果您不想根据某些条件处理所有这些,我建议您创建一个您想继续调用的函数。

(function process(index) {
  if (index >= group.entries.length) {
    return;
  }
  fetchEntry(group.entries[index])
    .then(function(entry) {
      if(entry.type === 'someType'){
        drift[entry._id] = getCoordinates(entry);
        // don't call the function again
      } else {
        process(index + 1);
      }
    });
})(0);

【讨论】:

  • 我想打破for 循环而不是forEach
  • @XeniaSis 我的错。不过,第二点阻止了这种情况的发生。
  • @XeniaSis 有解决办法。我现在正在写。马上起床。
  • 将对其进行测试并回复您
  • 如何从初始函数返回漂移对象?因为据我了解,该函数将在其他所有内容完成之前返回
【解决方案2】:

传递给 then() 的函数不会在 for 循环中调用。在 for 循环完成后(长时间)调用它。这就是异步编程模型的精髓。

您需要重新组织代码,以便不使用 for 循环。相反,您需要在回调中启动下一次提取,或者适当地不启动它。

PS。出于同样的原因,您也不能返回由回调填充的对象:您的函数将返回空对象 /before/ 回调被调用


编辑: 演示,但代码未经测试:

function getDrift(groups) {
    var promise = ...;

    var drift = {};
    groups.forEach(function(group) {
        if(group.type === 'something') {
            var i = 0;

            var processEntry = (function(entry) {
                     if(entry.type === 'someType'){
                         drift[entry._id] = getCoordinates(entry);

                         // We are finished, so complete our promise with
                         // the collected data
                         promise.success(drift);
                         return;
                     }

                     // increment our position in the array
                     i += 1;

                     // check to see if we are at the end of the array
                     if (i >= group.entries.length)
                        { return; }

                     // now fetch the next entry from the array
                    fetchEntry(group.entries[i].id)
                            .then(processEntry);
                 });

            // fetch the first entry
            fetchEntry(group.entries[i].id)
                 .then(processEntry);

            } // end if

    }); // end forEach()

    return promise;
}

【讨论】:

  • 我想通了,但我不确定该怎么做。还是谢谢!
  • 我用一些代码更新了我的答案,但它需要完成一些处理来填写部分承诺处理:创建承诺,并在成功时将数据传递给它。它显示了您需要如何将行为封装在函数中,并在回调中异步处理数组,而不是在循环中同步处理。
【解决方案3】:

如果满足 if 检查,我想打破当前的循环 group.entries 并继续下一组。

如果问题解释正确,请尝试使用 Promise.all()Array.prototype.map()

function getDrift(groups) {
  var drift = {};
  var m = 0;
  return Promise.all(groups.map(function(p) {
    // if `p` contains `5` return `null`
    // else multiply item within `p` by `10`
    return p.indexOf(5) === -1 ? p.map(function(k) {
      drift[m] = k * 10;
      ++m
    }) : null
  })).then(function(n) {
    return drift
  }, function(err) {
    console.log(err)
  })

}
// array containing `5` will not be processed
var res = getDrift([[1,2,3], [4,5,6], [7,8,9]])
.then(function(data) {
  console.log(data)
})

在问题中将上述模式应用于js

function getDrift(groups) {
  var drift = {};
  return Promise.all(groups.map(function(group) {
    return group.type === "something" 
    ? Promise.all(group.entries.map(function(g, index) {
      return fetchEntry(g.id).then(function(entry) {
         if (entry.type === "someType") {
            drift[entry._id] = getCoordinates(entry);
            return Promise.reject(entry.type);
         }
      })
    })) 
    : null
  })).then(function() {
    return drift
  }, function(err) {
    console.log(err)
  })

}
// array containing `5` will not be processed
var res = getDrift(groups)
.then(function(data) {
  console.log(data)
}, function(err) {
  console.log(err)
})

【讨论】:

  • 这不会停止循环,因为它是一个异步函数。
  • @guest271314 如果if 成功,我想打破循环,不要什么都不做
  • @XeniaSis 是什么意思“继续下一组。”
  • @guest271314 如果您在代码中看到,我正在循环一些组(第一个 forEach 不是 for 循环)
  • @XeniaSis 可以在问题中包含groups 对象的示例吗?
【解决方案4】:

这是可能的没有递归,但不是特别简单。

您可以使用以下组合:

  • forEach() 替换为Array#map() 以将groups 映射到一组promise,
  • 将原来的for 循环替换为Array#reduce() 以构建.then() 链,通过将其发送到错误路径,该链是“可破坏的”。

到目前为止,最简单的事情是根据问题中的代码坚持使用 drift 作为外部变量。也可以通过 Promise 链传递数据,但代码会涉及更多。

function getDrift(groups) {
    var drift = {};
    // Map groups to an array of promises.
    var promises = groups.map(function(group) {
        if(group.type === 'something') {
            // Here, replace the original `for` loop with `group.entries.reduce()` to build a .then() chain.
            return group.entries.reduce(function(p, e) {
                return p.then(function() {
                    return fetchEntry(e.id).then(function(entry) {
                        if(entry.type === 'someType') {
                            drift[entry._id] = getCoordinates(entry); // Yay!
                            throw false; // Throw almost anything here to skip the rest of the .then() chain.
                        } else {
                            return true; // Return almost anything here to carry on down the chain.
                        }
                    });
                });
            }, Promise.resolve()) // Resolved starter promise for the reduction.
            .catch(function() { // If there was a match, we end up on the error path, and need to convert to success.
                return true; // Return almost anything here.
            });
        } else {
            return true; // Return almost anything here to make a positive entry on the `promises` array.
        }
    });
    return Promise.all(promises) // Aggregate the promises.
    .then(function() {
        return drift; // and deliver the populated `drift` to the calling function
    });
}

由于getDrift() 返回一个承诺,drift 仅在 .then() 回调中可供调用方使用:

getDrift(groups).then(function(drift) {
    //do something with drift
});

【讨论】:

    猜你喜欢
    • 2019-10-25
    • 2021-09-09
    • 1970-01-01
    • 2023-04-06
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 2022-10-16
    • 2020-07-27
    相关资源
    最近更新 更多