使用 deferreds 动态添加顺序操作

Dynamically adding sequential actions using deferreds

这是我当前的代码:https://gist.github.com/benjamw/f6d5d682caddd4c1e506

我想做的是:根据用户点击页面时 URL 的内容,从服务器拉取不同的数据,完成后,使用 Hogan 渲染页面.

我的问题是步骤 B 需要步骤 C 的数据才能正确呈现,步骤 C 需要步骤 B 的数据才能提取正确的数据,但是如果用户请求的页面是步骤 C,则可以自行提取 (当直接转到 C 时,C 正确提取所需的数据是 URL 的一部分)。

我有一个 Deferred 存储在 Thing 中,它在各种拉取步骤中得到解决并触发渲染,但在 pull_B 中,我不希望它解决,直到它从 pull_Bpull_C 获取并清理数据。但是如果用户直接通过 C,那么我希望它能够很好地解析。

当进程经过B路径包含C路径时,如何在init()函数中为when动态添加promise?

或者,我怎样才能让 B 将它的承诺传递给 C,然后在那里解决它,但仍然保持能够通过 C 本身的功能,并且让它仍然在不通过 B 的情况下解决主要的延迟对象第一个?

我真的很努力不为此陷入回调地狱,但我发现这样做很难。

问题的症结显然是B和C之间的关系,总结起来似乎是:

  • 如果B,pull-C().then(pull-B)
  • 如果C,pull-B().then(pull-C);

在目前的尝试中,您 运行 通过尝试对 pull-B()pull-C() 中的流程逻辑进行编码而遇到了问题,这最终是可能的,但很复杂。

一个更简单的策略是使 pull_X() 函数非常简单——承诺返回数据检索器,并在 .init() 中的 switch/case 结构内编写流逻辑和数据清理代码。您将在下面的代码中看到我的意思。

除了更简单之外,这还将避免 pull_B()pull_C() 之间出现循环依赖的任何机会。

通过充分利用承诺,您还会发现:

  • this.dfd 的需求消失了(有利于从函数返回承诺)。
  • this.data 的需求消失了(有利于允许承诺交付数据)。
  • 将回调传递给 .pull() 的需求消失了(有利于在调用者中链接 .then())。因此,回调地狱消失了。

试试这个:

(function($) {
    "use strict";
    /**
     * The Thing
     *
     * @constructor
     */
    function Thing( ) {
        /* properties */ 
        this.url = [
            /* path */,
            /* id */
        ];
    }
    Thing.prototype.pull = function(url, args, type) {
        return $.ajax({
            type: type || 'GET',
            url: foo.root + url,
            data: $.extend({}, args || {}),
            dataType: 'json'
        });
    };
    Thing.prototype.pull_As = function() {
        return this.pull('a', this.query);
    };
    Thing.prototype.pull_A = function() {
        this.nav = false;
        return this.pull('a/'+ this.url[2]);
    };
    Thing.prototype.pull_B = function() {
        return this.pull('b/' + this.url[2]);
    };
    Thing.prototype.pull_C = function(id) {
        return this.pull('c/' + id || this.url[2]);
    };
    Thing.prototype.pull_D = function() {
        return this.pull_As();
    };
    Thing.prototype.render = function(data) {
        var i, len, html,
            that = foo.thing, /* because 'this' is the promise object */
            title = document.title.split('|');
        for (i = 0, len = title.length; i < len; i += 1) {
            title[i] = $.trim(title[i]);
        }
        title[0] = $.trim(that.title);
        document.title = title.join(' | ');
        html = Hogan.wrapper.render({
            'data': data,
        });
        $('#thing_wrapper').empty().append(html);
    };
    Thing.prototype.init = function( ) {
        var promise,
            that = this;
        switch (this.url[1].toLowerCase( )) {
            case 'a':
                promise = this.pull_A().then(function(data_A) {
                    /* ... do A data cleanup */ 
                    return data_A;//will be passed through to .render()
                });
                break;
            case 'b':
                promise = this.pull_C().then(function(data_C) {
                    //Here an inner promise chain is formed, allowing data_C, as well as data_B, to be accessed by the innermost function.
                    return that.pull_B().then(function(data_B) {
                        var data = ...;//some merge of data_B and data_C
                        return data;//will be passed through to .render()
                    });
                });
                break;
            case 'c':
                var id = ???;
                promise = this.pull_C(id).then(function(data_C) {
                    /* ... do C data cleanup */ 
                    return data_C;//will be passed through to .render()
                });
                break;
            case '':
            default:
                promise = this.pull_D().then(function(data_D) {
                    /* ... do D data cleanup */ 
                    return data_D;//will be passed through to .render()
                });
        }
        promise.then(this.render, console.error.bind(console));
    };
    window.Thing = Thing;
})(jQuery);

请特别注意从各种函数返回的承诺或数据。

我怀疑我的尝试是否 100% 正确。整体结构应该没问题,但您需要仔细查看细节。我可能误解了 B/C 依赖项。运气好的话,它会比我编码的更简单。

编辑:根据以下评论修改了代码。