【问题标题】:Shall I always invoke either JQuery Deferred.resolve or Deferred.reject?我应该总是调用 JQuery Deferred.resolve 或 Deferred.reject 吗?
【发布时间】:2013-12-19 03:28:20
【问题描述】:

所以如下代码,它只处理ajax调用成功的情况,如果ajax调用失败,它会被忽略并且永远不会调用deferred.reject()。那么如果我们遇到了失败的情况,会不会有jQuery内部的事件监听器会被永久保留而导致内存泄漏呢?

$.when(loadSomething).done(function() {
    // ...
});

function loadSomething() {
    var deferred = $.Deferred();

    // Only deal with the success case,
    // If the ajax call failed, it is ignored and the deferred.reject() will never be invoked.
    // So if we meet the failed case, will there any event listener inside jQuery will be keeped 
    // forever?
    ajaxCallToLoad(onResult);

    function onResult() {
        deferred.resolve();
    }

    return deferred.promise();
}

【问题讨论】:

  • 如果您只是简单地进行 ajax 调用,$.ajax/$.post/$.get/$.getJSON 返回一个与 Deferred 方法兼容并自动解析为的 jqXHR 对象你。因此,您可以从 ajaxCallToLoad 返回该对象,而不是创建另一个 deferred。
  • 谢谢,我无法控制ajaxCallToLoad 的行为是有原因的。你的意思是如果我创建了一个延迟对象,我应该总是可以确保稍后调用已解决或拒绝?这会导致内存泄漏吗?我曾尝试阅读Jquery的源代码,但它确实很复杂......
  • 它是否会导致内存泄漏取决于 Deferred 的实现,但是是的,最好总是解决或拒绝它。

标签: javascript jquery


【解决方案1】:

那么如果我们遇到了失败的情况,会不会有jQuery内部的事件监听器会被永久保留而导致内存泄漏?

几乎肯定不会。

但如果你这样做,你就明确地使用了 Promise 语义,但却违反了 Promise 契约。那是不好的做法。您必须选择“最佳实践”选项:

  1. 继续使用承诺,但要遵守合同。更新 ajaxCallToLoad 以便它也通知您失败,并在发生这种情况时在您的 deferred 上调用 reject。 (如果ajaxCallToLoad 使用jQuery 的$.ajax 函数,你可以只使用$.ajax 返回的jqXHR 对象;它实现了Promise。)

  2. 如果您不想履行承诺合同,只需使用普通的“成功”回调而不是承诺。

【讨论】:

  • 感谢您的重播,我做了一些测试,将$.when($.Deferred()).done(function() {}) 放入一个循环中,并检查它使用的内存的 chrome 任务管理器,它上下浮动大约 96MB。看起来垃圾收集适用于它。你的回答证明了,谢谢
【解决方案2】:

要回答您真正的问题,如果您在代码中的某处维护对延迟和/或承诺的引用,无论您是否解决或拒绝延迟,您只会泄漏内存。如果没有引用,那么它将像往常一样被垃圾收集。

也就是说,我认为在这种情况下,您应该使用 .then 方法来转换/过滤结果,而不是手动构建 Deferred。为了这个例子,让我们创建一个名为 load 的方法,它只是随机解析或拒绝延迟,类似于可能失败的 ajax 请求。

function load() {
    return $.Deferred(function( dfr ) {
       if ( Date.now() % 2 ) {
          dfr.resolve( 1 );
       }
       else {
          dfr.reject( "OHNO!" );
       }
    }).promise();
}

您现在可以使用 .then 来过滤它:

var filteredResult = load().then(function( result ) {
    return result * 2;
});

filteredResult 现在是一个承诺,如果解决了负载,它将使原始结果翻倍,因此filterResult.done( console.log.bind( console ) ) 将在控制台打印2。如果加载失败/被拒绝,那么失败处理程序仍然可以正常工作。

【讨论】:

    猜你喜欢
    • 2010-11-05
    • 2015-11-10
    • 1970-01-01
    • 2012-01-10
    • 2011-01-24
    • 1970-01-01
    • 2012-03-20
    • 2019-07-20
    • 2011-06-26
    相关资源
    最近更新 更多