使用 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_B
和 pull_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 依赖项。运气好的话,它会比我编码的更简单。
编辑:根据以下评论修改了代码。
这是我当前的代码:https://gist.github.com/benjamw/f6d5d682caddd4c1e506
我想做的是:根据用户点击页面时 URL 的内容,从服务器拉取不同的数据,完成后,使用 Hogan 渲染页面.
我的问题是步骤 B 需要步骤 C 的数据才能正确呈现,步骤 C 需要步骤 B 的数据才能提取正确的数据,但是如果用户请求的页面是步骤 C,则可以自行提取 (当直接转到 C 时,C 正确提取所需的数据是 URL 的一部分)。
我有一个 Deferred
存储在 Thing
中,它在各种拉取步骤中得到解决并触发渲染,但在 pull_B
中,我不希望它解决,直到它从 pull_B
和 pull_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 依赖项。运气好的话,它会比我编码的更简单。
编辑:根据以下评论修改了代码。