jquery的promise该怎么用?

自己在尝试使用jquery的promise对象的时候,出现了这个问题:

var promise1 = someAjax(); //someFunction返回jq的jqxhr对象
var promise2 = promise1.then(function (data) {
    return ajax1(); //返回jq的jqxhr对象
}, function (xhr) {
    return ajax2(); //返回jq的jqxhr对象
});

promise2.then(function (data) {
    // 我要怎么知道
    // 到底是ajax1执行成功了
    // 还是ajax2执行成功了?
}, function (xhr) {
    alert("error");
});

如上代码所示,promise1成功之后,我希望它执行ajax1();promise1失败之后,我希望它执行ajax2()。但是promise2的话,无论是ajax1()成功了还是ajax2()成功了,它都会走到自己的success的分支里对吧。

我要如何区别呢?

阅读 14.5k
5 个回答

自己问的问题,还是自己来答一下吧,看到还是有好几个人关注这个问题的。
在回答之前,我再加一个问题,是我在探索的时候遇到的。

var promise1 = someAjax(); //someFunction返回jq的jqxhr对象
var promise2 = promise1.then(function (data) {
    return ajax1(); //返回jq的jqxhr对象
    return ajax2(); //返回jq的jqxhr对象
}, function (xhr) {
    // nothing here
});

promise2.then(function (data) {
    // ajax1执行成功,
    // 和ajax2执行成功之后,
    // 我如何针对它俩进行不同的操作?
}, function (xhr) {
    alert("error");
});

也就是说,现在我们有两个问题。

问题一(我原本的问题):我想在执行某个ajax之后,成功的话,执行ajax1,失败的话,执行ajax2,那么如何分别对ajax1和ajax2的后续进行操作?

问题二(我追加的问题):我想在执行某个ajax之后,成功的话,执行ajax1和ajax2,那么如何分别对ajax1和ajax2的后续进行操作?

在stackoverflow上面,这个回答给了我启发,是的,使用$.when()$.Deferred()
不废话,直接上代码。

解决问题一:

// 为每一个ajax请求声明一个变量,并设为Deferred对象
// 这一步是必须的,而且必须在$.when前面
// 因为$.when的参数得是一个Deferred对象
var someAjaxDfd = $.Deferred();
var ajax1Dfd = $.Deferred();
var ajax2Dfd = $.Deferred();

// 这是程序主体
// 首先执行someAjax
someAjax(someAjaxDfd);
$.when(someAjaxDfd).then(function (data) {//这里的参数为什么是data,因为在下面的ajax函数中,我把data当成resolve()的参数传过来了
    // someAjax完成之后,如果成功,执行ajax1
    ajax1(ajax1Dfd);
}, function (xhr) { //这里的参数为什么是xhr,因为在下面的ajax函数中,我把xhr当成reject()的参数传过来了
    // 如果失败,执行ajax2
    var data = {/* some data */};
    ajax2(data, ajax2Dfd);
});

$.when(ajax1Dfd).then(function (data) {
    // ajax1成功之后执行的命令
}, function (xhr) {
    // ajax1失败之后执行的命令
});

$.when(ajax2Dfd).then(function (data) {
    // ajax2成功之后执行的命令
}, function (xhr) {
    // ajax2失败之后执行的命令
});


// 这里是三个ajax函数声明
function someAjax(dfd) {
    $.ajax({
        type: "GET",
        url: "/someajaxurl",
        contentType: "application/json",
        dataType: "json",
        success: function (data) {
            dfd.resolve(data);
        },
        error: function (xhr) {
            dfd.reject(xhr);
        }
    });
}

function ajax1(dfd) {
    $.ajax({
        type: "GET",
        url: "/ajax1url",
        contentType: "application/json",
        dataType: "json",
        success: function (data) {
            dfd.resolve(data);
        },
        error: function (xhr) {
            dfd.reject(xhr);
        }
    });
}

function ajax2(data, dfd) {
    $.ajax({
        type: "POST",
        url: "/ajax1url",
        contentType: "application/json",
        dataType: "json",
        data: JSON.stringify(data),
        success: function (data) {
            dfd.resolve(data);
        },
        error: function (xhr) {
            dfd.reject(xhr);
        }
    });
}

所以我的思路的原理是,首先声明一个Deferred对象,然后把这个对象传入到ajax的函数里。如果success,那么传入的Deferred调用resolve()方法,如果error,那么传入的Deferred调用reject()方法。然后在程序中用$.when()监听这个Deferred对象,成功或失败,执行不同的指令。

所以解决问题二,和问题一大同小异:

var someAjaxDfd = $.Deferred();
var ajax1Dfd = $.Deferred();
var ajax2Dfd = $.Deferred();

// 这是程序主体
// 首先执行someAjax
someAjax(someAjaxDfd);
$.when(someAjaxDfd).then(function (data) {
    // someAjax完成之后,如果成功,执行ajax1和ajax2
    ajax1(ajax1Dfd);
    var data = {/* some data */};
    ajax2(data, ajax2Dfd);
}, function (xhr) { 
    // nothing here
});

$.when(ajax1Dfd).then(function (data) {
    // ajax1成功之后执行的命令
}, function (xhr) {
    // ajax1失败之后执行的命令
});

$.when(ajax2Dfd).then(function (data) {
    // ajax2成功之后执行的命令
}, function (xhr) {
    // ajax2失败之后执行的命令
});

另外,如果ajax请求返回的json格式不对,即使status为200,$.ajax()也是会走到error的eventHandler里面的,这个要注意。至于什么样的格式是错的,反正我踩到的坑是是一个请求什么json都没返回,就走到error里面了。搞得我郁闷好几天,还以为自己写错了。

按照题目里的合并分支处理的话大概是这样的

someAjax()
  .then(function() {
      return ajax1();
  }, function() {
      return ajax2().then(function(result2) {
        return {ajax2: true, result2: result2};
      });
  })
  .then(function(result) {
    if(result.ajax2) {
      //play with result.result2
    } else {
      //play with result
    }
  }, function (xhr) {
    alert("error");
  });

可能其实不需要合并起来以后再if分开处理,直接这样也行

someAjax()
  .then(function() {
      return ajax1().then(function(result1) {
        //play with result1
        return play1(result1);
      });
  }, function() {
      return ajax2().then(function(result2) {
        //play with result2
        return play2(result2);
      });
  })
  .fail(function() {
    //ajax1 failed
    //or someAjax & ajax2 failed
  });

至于when$.Deferred,前者一般用于合并并发请求,后者一般用于在非Ajax的场景创建promise对象(比如setTimeout/DOM事件之类)

promise2.then(function (data) {
    // ajax1执行成功了会进这里
    // ajax2执行成功了不会进这里
}, function (xhr) {
    alert("error");
});

外边搞个变量,promise1的时候打个标记?

ajax1/ajax2 执行完了返回不同的数据值,
在promise2.then 中的onFulfilled函数中判断输入的参数值就好区分开来了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题