【问题标题】:How to use jQuery Deferred/Promise with a second $.ajax() after a timeout超时后如何使用带有第二个 $.ajax() 的 jQuery Deferred/Promise
【发布时间】:2016-08-07 08:22:51
【问题描述】:

我有一个绑定到$someEl 的事件处理程序,它应该只在oRes 填充数据时执行,这些数据可以来自主要来源 (getData1()) 或备用来源 (getData2())。仅当主源超时时才从备份源请求数据。

如果主源没有超时,那么一切正常;然而,当备份getData2() 函数被调用时,dfd 永远不会被解析,所以当我点击$someEl 时没有任何记录。我怀疑它不起作用,因为 getData2() 中的 Deferred 对象正在覆盖 $someEl 点击处理程序所指的 dfd 变量。

我感觉我没有使用“最佳实践”模式来应用 Deferred/Promise。在这种情况下,如何让点击处理程序在超时后正确等待从主 AJAX 响应或辅助 AJAX 响应填充 oRes?

一些澄清说明:

  • getData1() 必须在文档准备好后执行
  • $someEl.click() 可能会在文档加载期间随时触发,因此需要在 $(document).ready() 之外定义事件处理程序
  • 我坚持使用 jQuery 1.7.1

代码如下:

var oRes, dfd;

// Get data from primary source
function getData1() {
  dfd = $.ajax({
    ...
    success: function(data) {
      oRes = data;
    },
    error: function(jqXHR, textStatus, errorThrown) {
      if (textStatus==='timeout') getData2();
    },
    timeout: 10000 // 10-second timeout
  });
}

// Get data from backup source
function getData2() {
  dfd = $.ajax({...});
}

$someEl.click(function() {
  dfd.done(function() {
    console.log('This should only log when oRes is ready');
  });
});

$(document).ready(function() {
  getData1();
});

我已经用这支笔模拟了我的情况:http://codepen.io/thdoan/pen/pyVyKj

基本上,我无法让事件处理程序输出“数据准备就绪!”当 oRes 被填充时,无需在页面加载后手动单击该框。

【问题讨论】:

  • 只是想指出,使用您当前的策略,每次单击 $someEl 都会将另一个回调绑定到延迟对象,因此当最终解决延迟时,它将运行您的回调 X 次,其中 X 是元素被点击的次数。这可能是您想要的行为,但只是想确保您意识到这一点。
  • @MattDiamond 感谢您指出这一点,非常好的一点:)。我已将代码更改为使用.one('click', ...)

标签: jquery promise jquery-deferred deferred


【解决方案1】:

我会将函数调用包含在 ajax 请求的回调中。

var oRes = null;
var dfd = null;

// Get data from backup source
function getData2() {
  dfd = $.ajax({...});
}

// Get the primary data source
function getData1() {
  dfd = $.ajax({
    success: function(data) {
        oRes = data;

        // set the click handler when oRes is successfully set
        $(someEl).click(function() {
            console.log('This should only log when oRes is ready');
        });
    },
    error: function(jqXHR, textStatus, errorThrown) {
      if (textStatus==='timeout') getData2();
    },
    always: function(){
        if (oRes==null){
            getData2();
        }
    }
    timeout: 10000 // 10-second timeout
  });
}

【讨论】:

  • 我很乐意这样做,不幸的是,我正在使用一个较大应用程序的一部分的代码,其中 $someEl 可能$(document).ready之前被点击,所以它的事件处理程序必须在它之外定义(因此在getData1() 之外)。我将编辑问题以增加清晰度。
  • 这不会有问题,因为点击事件是在加载javascript之后分配的。如果 DOM 还没有完成并且用户点击了,那么他们很可能会在看到所有内容都加载后重试。
  • chriswirz,我用这支笔模拟了我的情况:codepen.io/thdoan/pen/pyVyKj——问题的关键是我需要在初始加载时触发对框的点击,但承诺是不兑现或其他东西阻止了初始日志记录。我想要发生的是,当填充 oData 时,事件处理程序消息应该说“数据准备就绪!”无需手动点击框。
【解决方案2】:

我认为你只需要把它提升一个层次。

预先创建一个 Deferred,供您的点击处理程序监听(并且该实例不会改变)。

然后您解决从 getData1()getData2() 延迟的问题:

var oRes,

    // Resolve this deferred when data has been successfully loaded:
    dfd = $.Deferred(),

     // But add listeners to this promise:
    dfdPromise = dfd.promise();

// Get data from primary source
function getData1() {
    $.ajax({
        ...
        success: function(data) {
            oRes = data;

            // Resolve when data loaded:
            dfd.resolve(data);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            if (textStatus==='timeout') getData2();
        },
        timeout: 10000 // 10-second timeout
    });
}

// Get data from backup source
function getData2() {
    $.ajax({...})
        .then(function(data){
            // Fallback data resolve:
            dfd.resolve(data);
        });
}

$someEl.click(function() {
    dfdPromise.done(function() {
        console.log('This should only log when oRes is ready');
    });
});

$(document).ready(function() {
    getData1();
});

【讨论】:

猜你喜欢
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 2012-11-03
  • 2019-08-01
  • 1970-01-01
  • 2015-07-17
  • 2015-07-25
  • 1970-01-01
相关资源
最近更新 更多