【发布时间】:2016-12-07 17:19:03
【问题描述】:
不是“反模式”:这不是常见的“反模式”,可以简单地通过直接返回嵌套的延迟对象来解决......这段摘录是对嵌套延迟加载的代码的重大简化,它将返回Deferred 在不久的将来,因此不可能直接从原始方法返回尚不存在的 Deferreds。
除此之外,从原始(顶级)方法返回的 Deferred 依赖于在不久的将来创建的多个 Deferred,其中只有“拒绝”被立即转发回来......
我发现自己最近经常写这样的东西:
My.prototype.init=function() {
var $dfd = $.Deferred();
this.initSomethingElse() // returns Deferred
.done(function() {
$dfd.resolve();
})
.fail(function() {
$dfd.reject();
});
return $dfd;
}
我写了一个简短的函数来在 Deferred 上添加 .link() 方法以简化如下:
My.prototype.init=function() {
var $dfd = $.Deferred();
this.initSomethingElse() // returns $.Deferred(addLinkMethodFce)
.link($dfd);
return $dfd;
}
但我认为它可能太常见了,所以其他人可能会想到它,我可能错过了 jQuery 中的一些开箱即用的解决方案。
问题:jQuery 能否将现有的 Deferred 对象与其他 Deferred 对象链接起来,如果链接的对象被解析/拒绝,当前的 Deferred 将以相同的方式被解析/拒绝?
类似于dfd1Promise = $.when(dfd2) 之类的东西,除了不需要创建新的Promise 而是简单地将现有对象“链接”在一起而不创建新对象dfd1.link(dfd2)。
谢谢。
EDIT#1:我看到很多我没有问过的问题的答案 - 比如“我应该使用 Deferred 吗?”我想澄清一下情况。
我正在使用异步加载的代码(My.prototype.init 确实加载了异步脚本和数据,使其将 Deferred 返回给调用者)。
而且该代码还依赖于异步第三方代码 - 我无法修改的 API - initSomethingElse() 也将在不久的将来解决并返回 Deferred。
关键是,如果该代码被拒绝,我的代码必须被拒绝。我希望有一些神奇的标准糖语法来重复$theirDfd.reject(function() {$myDfd.reject.call(...);});,仅此而已。
有没有一种标准的方式来处理我的$myDfd.link($theirDfd);?
我只是大大简化了情况以用几行来表达它,所以不要被欺骗认为我可能不需要 Deferred 或其他我没有问过的东西......
EDIT#2:为了进一步阐明 .link() 的作用,这是我目前使用的帮助代码:
/**
* Adds some syntax sugar methods to Deferred object.
*
* Deferred.link(dfd, ...) - if this object resolves/rejects/progresses it will resolve/reject/progress linked object as well
* Deferred.linkResolve(dfd, ...) - if this object resolves it will resolve linked object as well
* Deferred.linkReject(dfd, ...) - if this object rejects it will reject linked object as well
* Deferred.linkProgress(dfd, ...) - if this object progresss it will progress linked object as well
*
* Methods can be appended to Deferred object by two ways:
*
* $dfd = edfd($.Deferred());
* $dfd = $.Deferred(edfd);
*
* @access public
* @return {Deferred}
*/
function edfd($dfd) {
/**
* Helper used by this.link(), this.linkReject(), this.linkProgress(), this.linkResolve()
*
* @access private
* @param {Boolean} accept link this Deferred's accept call to target's accept
* @param {Boolean} reject link this Deferred's reject call to target's reject
* @param {Boolean} progress link this Deferred's progress call to target's progress
* @param {Object} targets array of Deferreds or array of arrays of Deferreds
* @return {Deferred} this (called in $dfd context)
*/
function linker(accept, reject, progress, targets) {
targets = dna.core.getOpts(targets, [['dfdList', 'object'], 'recursive']);
for (var i = 0; i < targets.dfdList.length; i++) {
var $link = targets.dfdList[i];
$dfd.then(
accept && function() {$link.resolve.apply($link, arguments);},
reject && function() {$link.reject.apply($link, arguments);},
progress && function() {$link.progress.apply($link, arguments);}
);
}
return this;
}
/**
* If link this Deferred's rejection/resolution/progress to all linked Deferreds.
*
* @access public
* @param {...Deferred} dfd jQuery Deferred objects or arrays of Deferred objects.
* @return {Deferred} this object
*/
$dfd.link = function() {
return linker(true, true, true, arguments);
};
/**
* If this Deferred is resolved then resolve also linked Deferreds.
*
* @access public
* @param {...Deferred} dfd jQuery Deferred objects or arrays of Deferred objects.
* @return {Deferred} this object
*/
$dfd.linkResolve = function() {
return linker(true, false, false, arguments);
};
/**
* If this Deferred gets rejected then reject also linked Deferreds.
*
* @access public
* @param {...Deferred} dfd jQuery Deferred objects or arrays of Deferred objects.
* @return {Deferred} this object
*/
$dfd.linkReject = function() {
return linker(false, true, false, arguments);
};
/**
* If this Deferred progresses then progress also linked Deferreds.
*
* @access public
* @param {...Deferred} dfd jQuery Deferred objects or arrays of Deferred objects.
* @return {Deferred} this object
*/
$dfd.linkProgress = function() {
return linker(false, false, true, arguments);
};
return $dfd;
}
EDIT#3:只为那些渴望看到特殊用途的人准备。对象的 deferred 是这样初始化的 this.$dfd = $.Deferred(edfd); (请参阅上面我对这个问题的解决方案 - function edfd(...)
/**
* Public interface as required for payment processors.
*
* @access public
* @param {Object} payData
* @return {Deferred} resolved/rejected based on payment result.
*/
Braintree.prototype.process = function(payData) {
this.cleanUp();
this.payData = payData;
this.createLayer();
this.initBraintree(payData.clientToken)
.linkReject(this.$dfd);
return this.$dfd;
};
【问题讨论】:
-
这是一个非常常见的反模式,在 JavaScript 中的新用户中很常见。请参阅以下内容:What is the explicit promise construction antipattern and how do I avoid it?
-
为什么需要
jQuery.Deferred()? -
您希望 .link 究竟能做什么?就目前而言,我认为您没有理由需要通过简单地返回现有承诺而无法使用的任何功能。
-
@KevinB 我已经用我的
.link()、.linkReject()、.linkResolve()、.linkProgress()糖语法方法的完整代码更新了我的问题。我的问题真的是这样的:是否有现有的事情在做同样的事情? (我可能会想念像能够在对象$dfd.when($dfd2)上调用$.when()导致同样的事情......或者我可能会想念的事情。) -
我仍然不明白你所有链接功能的目的。它仍然只是看起来像反模式
标签: javascript jquery jquery-deferred