在承诺的 then() 中打破 for 循环
Break for loop inside then() of a promise
我遇到了一个奇怪的情况,我想在收到 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
是 return 基于 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 的建议进行递归和回调 return 所需的值。谢谢大家!
这是不可能的,因为 Promises 是异步的,这意味着 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);
您传递给 then() 的函数不会在 for 循环内调用。它在 for 循环完成后被调用 (long)。这就是异步编程模型的本质。
您需要重新组织代码,以便不使用 for 循环。相反,您需要在回调中启动下一次提取,或者不适当地启动它。
PS。出于同样的原因,您也不能 return 由回调填充的对象:您的函数将 return 空对象 /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;
}
If the if check is satisfied, I want to break the loop on the current
group.entries and continue on to the next group.
如果正确解释问题,请尝试使用 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)
})
这是可能的无需递归,但不是特别简单。
您可以组合使用:
- 将
forEach()
替换为 Array#map()
以将 groups
映射到一组承诺,
- 将原来的
for
循环替换为 Array#reduce()
以构建 .then()
链,即 "breakable" 通过将其发送到错误路径。
到目前为止最简单的事情是按照问题中的代码坚持使用 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()
returns 承诺,drift
仅在 .then() 回调中对调用者可用:
getDrift(groups).then(function(drift) {
//do something with drift
});
我遇到了一个奇怪的情况,我想在收到 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
是 return 基于 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 的建议进行递归和回调 return 所需的值。谢谢大家!
这是不可能的,因为 Promises 是异步的,这意味着 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);
您传递给 then() 的函数不会在 for 循环内调用。它在 for 循环完成后被调用 (long)。这就是异步编程模型的本质。
您需要重新组织代码,以便不使用 for 循环。相反,您需要在回调中启动下一次提取,或者不适当地启动它。
PS。出于同样的原因,您也不能 return 由回调填充的对象:您的函数将 return 空对象 /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;
}
If the if check is satisfied, I want to break the loop on the current group.entries and continue on to the next group.
如果正确解释问题,请尝试使用 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)
})
这是可能的无需递归,但不是特别简单。
您可以组合使用:
- 将
forEach()
替换为Array#map()
以将groups
映射到一组承诺, - 将原来的
for
循环替换为Array#reduce()
以构建.then()
链,即 "breakable" 通过将其发送到错误路径。
到目前为止最简单的事情是按照问题中的代码坚持使用 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()
returns 承诺,drift
仅在 .then() 回调中对调用者可用:
getDrift(groups).then(function(drift) {
//do something with drift
});