javascript 递归链接 Promise
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12291003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Chaining Promises recursively
提问by Zane Geiger
I'm working on a simple Windows 8 app in which I need to fetch a set of data from a web site. I am using WinJS.xhr() to retrieve this data, which returns a Promise. I then pass a callback into this Promise's .then() method, which supplies my callback with the returned value from the asynchronous call. The .then() method returns another Promise, giving it the value that my callback returns. The basic structure of such a query would be as follows:
我正在开发一个简单的 Windows 8 应用程序,我需要在其中从网站获取一组数据。我正在使用 WinJS.xhr() 来检索此数据,它返回一个 Promise。然后我将回调传递给这个 Promise 的 .then() 方法,该方法为我的回调提供异步调用的返回值。.then() 方法返回另一个 Promise,给它我的回调返回的值。这种查询的基本结构如下:
WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
//do stuff
return some_value;
}).then(
function secondcallback( some_value )
{
//do stuff
});
In my situation, however, I may need to make additional queries for data depending on the data returned by the first query, and possibly more queries depending on THAT data... and so on, recursively.
但是,在我的情况下,我可能需要根据第一个查询返回的数据对数据进行其他查询,并且可能需要根据 THAT 数据进行更多查询......等等,递归。
I need a way to code this such that the final .then() is not executed until ALL the recursions have completed, similar to this:
我需要一种编码方法,以便在所有递归完成之前不执行最终的 .then() ,类似于:
function recurse() {
return WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
if( result_from_xhr == something )
{
recurse();
}
});
}
recurse().then(
function final()
{
//finishing code
});
The problem is that, of course, the finishing code is called as soon as the first level of recursion completes. I need some way to nest the new promise and the old promise from within the callback.
问题是,当然,一旦第一级递归完成,就会调用完成代码。我需要某种方式在回调中嵌套新的承诺和旧的承诺。
I hope my question is clear enough, I'm really not sure how to explain it and frankly the idea of asynchronous recursive code makes my head hurt.
我希望我的问题足够清楚,我真的不知道如何解释它,坦率地说,异步递归代码的想法让我很头疼。
回答by Chris Tavares
What I would do here is to create a whole new, standalone promise that you can complete manually, and return that from the recurse() function. Then, when you hit the point that you know you're done doing async work, complete that promise.
我在这里要做的是创建一个全新的、独立的 promise,您可以手动完成它,并从 recurse() 函数返回它。然后,当您知道自己已完成异步工作时,请完成该承诺。
Promise.join works when you've got a known set of promises - you need the entire array of promises available before you call join. If I followed the original question, you have a variable number of promises, with more possibly popping up as part of async work. Join isn't the right tool in these circumstances.
Promise.join 当您有一组已知的承诺时起作用 - 在您调用 join 之前,您需要整个可用的承诺数组。如果我按照最初的问题进行操作,您将拥有可变数量的承诺,并且更有可能作为异步工作的一部分出现。在这些情况下,Join 不是正确的工具。
So, what does this look like? Something like this:
那么,这看起来像什么?像这样的东西:
function doSomethingAsync() {
return new WinJS.Promise(function (resolve, reject) {
function recurse() {
WinJS.xhr({ url: "http://www.example.com/" })
.then(function onResult(result_from_xhr) {
if (result_from_xhr === something) {
recurse();
} else {
// Done with processing, trigger the final promise
resolve(whateverValue);
},
function onError(err) {
// Fail everything if one of the requests fails, may not be
// the right thing depending on your requirements
reject(err);
});
}
// Kick off the async work
recurse();
});
}
doSomethingAsync().then(
function final()
{
//finishing code
});
I rearranged where the recursion is happening so that we aren't recreating a new promise every time, thus the nested recurse() function instead of having it at the outer level.
我重新安排了递归发生的位置,这样我们就不会每次都重新创建一个新的承诺,因此嵌套的 recurse() 函数而不是在外部级别使用它。
回答by TKoL
I solved this problem in perhaps a different way (I think it's the same problem) while making my own Windows 8 app.
在制作我自己的 Windows 8 应用程序时,我可能以不同的方式解决了这个问题(我认为这是同一个问题)。
The reason I came across this problem is because, by default, a query to a table will paginate, and only return 50 results at a time, so I created a pattern to get the first 50, and then the next 50, etc, until a response comes back with less than 50 results, and then resolve the promise.
我遇到这个问题的原因是,默认情况下,对表的查询会分页,并且一次只返回 50 个结果,所以我创建了一个模式来获取前 50 个,然后是接下来的 50 个,依此类推,直到返回少于 50 个结果的响应,然后解决承诺。
This code isn't going to be real code, just for illustration:
这段代码不会是真正的代码,只是为了说明:
function getAllRows() {
return new WinJS.Promise(function(resolve, reject){
var rows = [];
var recursivelyGetRows = function(skipRows) {
table.skip(skipRows).read()
.then(function(results){
rows = rows.concat(results);
if (results.length < 50) {
resolve(rows);
} else {
recursivelyGetRows(skipRows + 50);
}
})
}
recursivelyGetRows(0);
});
}
so getAllRows() returns a promise that resolves only after we get a result back with less than 50 results (which indicates it's the last page).
所以 getAllRows() 返回一个承诺,只有在我们得到少于 50 个结果的结果(这表明它是最后一页)后才会解决。
Depending on your use case, you'll probably want to throw an error handler in there too.
根据您的用例,您可能还想在其中抛出一个错误处理程序。
In case it's unclear, 'table' is a Mobile Services table.
如果不清楚,“表”是移动服务表。
回答by Jeff Brand
You will need to use Promise.join().done() pattern. Pass an array of Promises to join(), which in your case would be a collection of xhr calls. The join() will only call done() when all of the xhr Promises have completed. You will get an array of results passed to the done(), which you can then iterate over and start again with a new Promise.join().done() call. The thing to be away of, when using this approach, is that if one of the Promises passed to join() fail, the entire operation is treated as an error condition.
您将需要使用 Promise.join().done() 模式。将一组 Promise 传递给 join(),在您的情况下,它是 xhr 调用的集合。join() 只会在所有 xhr Promises 完成后调用 done() 。您将获得传递给 done() 的结果数组,然后您可以迭代这些结果并使用新的 Promise.join().done() 调用重新开始。使用这种方法时要避免的是,如果传递给 join() 的 Promise 之一失败,则整个操作将被视为错误条件。
Sorry I don't have time right now to try and stub out the code for you. If I get a chance, I will try to later. But you should be able to insert this into your recursive function and get things to work.
抱歉,我现在没有时间尝试为您删除代码。如果有机会,我会在以后尝试。但是您应该能够将其插入到您的递归函数中并使事情开始工作。
回答by Zane Geiger
Well, I solved my problem; my recursive function was misinterpreting the data and thus never stopped recursing. Thank you for your help, and I'll be sure to watch those screencasts, as I still don't quite fully grasp the Promise chaining structure.
好吧,我解决了我的问题;我的递归函数误解了数据,因此从未停止递归。感谢您的帮助,我一定会观看这些截屏视频,因为我还没有完全掌握 Promise 链结构。