【问题标题】:Chaining deferreds链接延迟
【发布时间】:2013-07-30 11:56:35
【问题描述】:

我有很多目前看起来像这样的函数:

function LoadFromURL(url) 
{
    var d = $.Deferred();
    $.get(url).then(function(text)
    {
        LoadFromText(text).then(function(obj)
        {
            d.resolve(obj);
        }, 
        function(err)
        {
            d.reject(err);
        });
    }, 
    function(err)
    {
        d.reject(err);
    });
    return d.promise();
}

这个函数接受一个 URL 并加载一个 Promise 中的文本。然后将文本传递给 LoadFromText 函数,该函数返回另一个承诺。如果成功,则外部延迟被解决。如果无法加载 URL,或者 LoadFromText 函数失败,则外部延迟被拒绝。

有没有一种方法可以链接解决/拒绝的结果并最终得到如下结果:

function LoadFromURL(url) 
{
    return $.get(url).then(function(text)
    {
        return LoadFromText(text);
    }, 
    function(err)
    {
        return $.Deferred().reject(err);
    }).promise();
}

我已经看到了一些允许这样做的承诺的实现——在“then”中返回一个承诺,意味着使用了承诺的最终结果。

我意识到目前这不起作用(结果是承诺,而不是承诺的结果),但希望有人可以提出一种方法来使这项工作,这样我就可以整理有时令人困惑的混乱第一个例子。

我希望这是有道理的。谢谢。

【问题讨论】:

  • jQuery 的 Deferred 的 then 方法与我使用的其他 Promise 库有点不同。它更适合用作过滤器。 (当然,你可以将它用于任何你想要的东西。)看看.when()。如果这不能满足您的要求,那么您的第一个代码块看起来就像我解决这个问题的方式。或者,包括 Async.js 什么的。

标签: jquery jquery-deferred deferred


【解决方案1】:

有没有办法可以链接结果

是的,这是then的默认行为!

function LoadFromURL(url) {
    return $.get(url).then(LoadFromText);
}

我已经看到了一些允许这样做的承诺的实现——在“then”中返回一个承诺,意味着使用了承诺的最终结果。

jQuery 是否 从 1.8 版开始支持这种行为。对于 1.5 到 1.7,您可以使用 .pipe

【讨论】:

    【解决方案2】:

    LoadFromURL 将简化如下:

    function LoadFromURL(url) {
        return $.get(url).then(function(text) {
            return LoadFromText(text);
        });
    }
    

    可能有你没有考虑过的问题...

    与您的原始版本一样,该过程往往会无限期地递归,直到服务器返回 HTTP 错误。您在适当地编写服务器资源方面会遇到一点挑战,尤其是在您可能需要访问最终交付的文本的情况下。

    稍加考虑,您可以通过在文本本身或 HTTP 响应代码中指示“继续”或“完成”的方式来传递所有文本。例如,您可以使用响应代码 202(“已接受”)来指示流程未完成。

    我认为这会起作用(使用 jQuery 1.5+):

    function LoadFromURL(url) {
        return $.get(url).then(function(text, textStatus, jqXHR) {
            if(jqXHR.status === 202) {//continue
                return LoadFromText(text);
            }
            else {//complete
                return text;
            }
        });
    }
    

    您只需要安排服务器返回 202 表示“继续”或 200 表示“完成”。

    因此,LoadFromURL() 可以如下调用:

    LoadFromURL('my/resource').then(function(text) {
        console.log("After a series of $.get requests, the finally delivered text was:\n" + text);
    });
    

    【讨论】:

    • 我的第一个示例一定是错误的,因为它似乎确实按照我的预期工作。谢谢。
    【解决方案3】:

    我不确定我是否理解正确,因为您的第二个函数LoadFromText 对我来说是某种黑匣子。不过,我认为您的构造对于执行的事情来说太臃肿了。尝试在更多的功能中做更少的事情。我做了一个命题:

    function Load() {
        var myUrl = '//path.to/url';
        LoadFromUrl(myUrl)
            .then(LoadFromText, errorHandler)
            .then(successHandler, errorHandler);
    }
    
    function successHandler(returnedData) {
        console.log('Requests were successful: ' + returnedData);
    }
    
    function errorHandler(err) {
        console.log('An error occurded: ' + err);
    }
    
    function LoadFromURL(url) {
        return $.get(url);
    }
    
    function LoadFromText(text) {
        // THIS IS SOME SORT OF BLACK BOX FOR ME...
        // SEEMS TO RETURN A PROMISE, THOUGH
    
        var $deferred = $.Deferred();
    
        // DO THE LOGIC TO EITHER REJECT OR RESOLVE THE PROMISE
    
        return $deferred.promise();
    }
    

    您只使用主函数Load 来启动ajax 链。一旦第一个 ajax 请求完成,第二个就从第一个的数据开始。之后,successHandler 将对第二个 ajax 调用的数据做任何需要的事情。这是对您的解决方案的重要补充。
    除此之外,它与您所做的基本相同,但分发到更易于维护的较小功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      相关资源
      最近更新 更多