【问题标题】:Cascading jQuery.Deferred [duplicate]级联 jQuery.Deferred [重复]
【发布时间】: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


【解决方案1】:

TL;DR

问题:jQuery 能否将现有的 Deferred 对象与其他 Deferred 对象链接起来,如果链接的对象被解析/拒绝,当前的 Deferred 将以相同的方式被解析/拒绝?

我明白你在想什么以及为什么;但是,ES2015 已经通过 Promise 链接为您解决了这个问题。很好的例子:Promise.all() (MDN link) 是此类功能的一个很酷的实现(请参阅:“快速失败”)。

如果需要,可以使用多个 deferred 并使用 jQuery.when() 将它们“链接”在一起

var d1 = $.Deferred();
var d2 = $.Deferred();

$.when( d1, d2 ).done(function ( v1, v2 ) {
    console.log( v1 ); // "Fish"
    console.log( v2 ); // "Pizza"
});

d1.resolve( "Fish" );
d2.resolve( "Pizza" );

(代码来源:https://api.jquery.com/jquery.when/


在 SO 上阅读此good answer 以了解您的问题(及其相关链接)。为什么答案对您的案例有帮助可能不是很明显,但请考虑一下并编写一些示例代码,您会更好地理解。

正如其他人所暗示的那样:

  • 如果可能的话,你想避免deferred
  • 使用.then()(最后是.catch())链接您的承诺

【讨论】:

  • 遗憾的是,没有神奇的语法糖。简短的回答:这种工作方式错误地使用了 Promises 的优点。你也可以试试看 RxJS,但这比你想要的要大。
  • 你能发布一个更清晰的代码示例吗?当您自己的问题有点神秘时,很难给您一个准确的答案。
  • 感谢您的帮助。但我再次重申 * 只是类似于 dfd1Promise = $.when(dfd2, ...) 的东西,除了不需要创建新的 Promise 而是简单地将现有对象“链接”在一起而不创建新对象 dfd1.link(dfd2) 。 * 我知道何时使用 Deferreds,谢谢。 ;-) * .then() 和 .catch() 结果与我要求解决的 .done() 和 .fail() 完全相同 - 所以这是在循环中
  • 酷。好吧,如果您自己找到答案,请尝试回帖。有趣的场景。
  • 我不是故意粗鲁的。我错过了您发布更清晰示例的请求。发布。但我的印象是,正如您已经指出的那样,没有真正神奇的语法糖。所以我认为我的问题的答案很简单:不,没有标准的语法糖,所以编写自己的语法糖并不是一件坏事,只要它不用于被认为是已知的 common 场景 反模式。我说的对吗?
猜你喜欢
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-21
  • 2018-07-09
  • 2018-04-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多