17

我正在开发一个简单的 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
    });

但是,在我的情况下,我可能需要根据第一个查询返回的数据对数据进行额外的查询,并且可能需要根据该数据进行更多查询......等等,递归。

我需要一种方法对此进行编码,以便在所有递归完成之前不执行最终的 .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
});

问题是,当然,一旦第一级递归完成,就会调用完成代码。我需要一些方法在回调中嵌套新的承诺和旧的承诺。

我希望我的问题足够清楚,我真的不知道如何解释它,坦率地说异步递归代码的想法让我很头疼。

4

4 回答 4

14

我在这里要做的是创建一个全新的、独立的 Promise,您可以手动完成它,并从 recurse() 函数中返回它。然后,当你知道你已经完成了异步工作时,完成那个承诺。

Promise.join 在你有一组已知的 Promise 时工作——你需要在调用 join 之前提供整个 Promise 数组。如果我按照最初的问题进行操作,那么您有不同数量的承诺,更有可能作为异步工作的一部分弹出。在这些情况下,加入不是正确的工具。

那么,这看起来像什么?像这样的东西:

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
});

我重新安排了递归发生的位置,这样我们就不会每次都重新创建一个新的 Promise,因此嵌套的 recurse() 函数而不是将它放在外部级别。

于 2012-09-06T22:50:58.070 回答
5

在制作自己的 Windows 8 应用程序时,我可能以不同的方式解决了这个问题(我认为这是同一个问题)。

我遇到这个问题的原因是,默认情况下,对表的查询将分页,并且一次只返回 50 个结果,所以我创建了一个模式来获取前 50 个,然后是下一个 50,等等,直到响应返回少于 50 个结果,然后解决承诺。

这段代码不是真正的代码,只是为了说明:

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);

    });
}

所以 getAllRows() 返回一个只有在我们得到少于 50 个结果的结果(这表明它是最后一页)后才会解析的承诺。

根据您的用例,您可能还想在其中抛出一个错误处理程序。

如果不清楚,“表”是移动服务表。

于 2015-07-04T16:54:06.520 回答
1

您将需要使用 Promise.join().done() 模式。将一组 Promises 传递给 join(),在您的情况下,这将是 xhr 调用的集合。join() 只会在所有 xhr Promise 完成后调用 done()。您将获得一个传递给 done() 的结果数组,然后您可以对其进行迭代并通过新的 Promise.join().done() 调用重新开始。使用这种方法时要避免的是,如果传递给 join() 的 Promise 之一失败,则整个操作将被视为错误条件。

抱歉,我现在没有时间尝试为您编写代码。如果有机会,我会在以后尝试。但是您应该能够将其插入到您的递归函数中并使事情正常工作。

于 2012-09-05T23:28:19.860 回答
0

好吧,我解决了我的问题;我的递归函数误解了数据,因此从未停止递归。感谢您的帮助,我一定会观看这些截屏视频,因为我还没有完全掌握 Promise 链结构。

于 2012-09-06T00:08:43.290 回答