如何在超时后使用 jQuery Deferred/Promise 和第二个 $.ajax()

How to use jQuery Deferred/Promise with a second $.ajax() after a timeout

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

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

我感觉我没有使用 "best practice" 模式来应用 Deferred/Promise。在这种情况下,您如何让点击处理程序正确等待 oRes 在超时后从主要 AJAX 响应或次要 AJAX 响应填充?

一些说明说明:

代码如下:

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 被填充时,我无法让事件处理程序输出 "Data is ready!",而无需在页面加载后手动单击该框。

我会将函数调用包含在 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
  });
}

我认为你只需要提高一个级别。

预先创建一个您的点击处理程序将监听的 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();
});