为什么jQuery的Promise.reject不起作用?

时间:2017-12-01 17:29:11

标签: javascript jquery ruby-on-rails ajax promise

我正在使用REST API,类似于这个存根:Snippet 1(Ruby on Rails示例)。

我在经典回调中有现有的jQuery代码:Snippet 2

使用日志执行:

case 1:
[INFO] /api/my/action1: got rejecting signal, do not continue 

case 2:
[INFO] /api/my/action1: no rejecting signal, continue
[INFO] /api/my/action2: no rejecting signal, continue
[INFO] /api/my/action3: hurrah!! we got message:  Third action executed!

case 3:
[INFO] /api/my/action1: no rejecting signal, continue
[ERROR] Got error with message: Unexpected error

我想将此代码重构为承诺:

function ajxGet(url){
  return $.ajax({
    url,
    dataType: 'JSON'
  })
}

export function makeThreeAsyncQueries(){
  ajxGet('/api/my/action1')
    .then(response1 => {
      if(response1.do_reject_other_actions){
        console.log('[INFO] /api/my/action1: got rejecting signal, do not continue');
        return Promise.reject({mute: true});
      }else{
        console.log('[INFO] /api/my/action1: no rejecting signal, continue');
        return ajxGet('/api/my/action2');
      }
    })
    .then(response2 => {
      if(response2.do_reject_other_actions){
        console.log('[INFO] /api/my/action2: got rejecting signal, do not continue');
        return Promise.reject({mute: true});
      }else{
        console.log('[INFO] /api/my/action2: no rejecting signal, continue');
        return ajxGet('/api/my/action3');
      }
    })
    .then(response3 => {
      console.log('[INFO] /api/my/action3: hurrah!! we got message: ', response3.message);
    })
    .fail((err) => {
      if(err && err.mute){
        console.log('[INFO] normal chain break.');
        return
      }
      console.info('[ERROR] Got error with message:', err.responseJSON.message);
    });
}

问题是Promise.reject({mute: true});不起作用,我有这些日志:

[INFO] /api/my/action1: got rejecting signal, do not continue           <<-- SHOULD STOP HERE
[INFO] /api/my/action2: no rejecting signal, continue
   Uncaught (in promise) Object {mute: true}
   <...callstack here...>
[INFO] /api/my/action3: hurrah!! we got message:  Third action executed!

1 个答案:

答案 0 :(得分:1)

在您的示例中,您使用Promise中的ECMAScript 2015 specification而不是jQuery的Deferred类似承诺的对象。

所以不是这一行:

return Promise.reject({mute: true});

使用此:

return $.Deferred().reject({ mute: true }) 

完整的代码示例:

function ajxGet(url){
  return $.ajax({
    url,
    dataType: 'JSON'
  })
}

export function makeThreeAsyncQueries(){
  ajxGet('/api/my/action1')
    .then(response1 => {
      if(response1.do_reject_other_actions){
        console.log('[INFO] /api/my/action1: got rejecting signal, do not continue');
        return $.Deferred().reject({ mute: true })
      }else{
        console.log('[INFO] /api/my/action1: no rejecting signal, continue');
        return ajxGet('/api/my/action2');
      }
    })
    .then(response2 => {
      if(response2.do_reject_other_actions){
        console.log('[INFO] /api/my/action2: got rejecting signal, do not continue');
        return $.Deferred().reject({ mute: true })
      }else{
        console.log('[INFO] /api/my/action2: no rejecting signal, continue');
        return ajxGet('/api/my/action3');
      }
    })
    .then(response3 => {
      console.log('[INFO] /api/my/action3: hurrah!! we got message: ', response3.message);
    })
    // as argument here we will get jquery's xhr object on AJAX-error, or will get payload sent by $.deferred().reject
    .fail((xhr) => {
      if(xhr && xhr.mute){
        console.log('[INFO] normal chain break.');
        return
      }
      console.info('[ERROR] Got error with message:', xhr.responseJSON.message);
    });
}

所以当后端返回do_reject_other_actions === true时,链会断开,你会得到这个正确的日志:

[INFO] /api/my/action1: got rejecting signal, do not continue
[INFO] normal chain break.

or

[INFO] /api/my/action1: no rejecting signal, continue
[INFO] /api/my/action2: got rejecting signal, do not continue
[INFO] normal chain break.

or

[INFO] /api/my/action1: no rejecting signal, continue
[INFO] /api/my/action2: no rejecting signal, continue
[INFO] /api/my/action3: hurrah!! we got message:  Third action executed!

or

[INFO] /api/my/action1: no rejecting signal, continue
[ERROR] Got error with message: Unexpected error 

解决方案2

如果您想使用ECMAScript2015 Promise,可以将jQuery&#39; ajax包装到Promise中:

function ajxGet(url){
  return new Promise((resolve, reject) => {
    $.ajax({
      url,
      dataType: 'JSON',
      success: response => resolve(response),
      error: (xhr) => { reject(xhr) },
    })
  });
}

export function makeThreeAsyncQueries(){
  ajxGet('/api/my/action1')
    .then(response1 => {
      if(response1.do_reject_other_actions){
        console.log('[INFO] /api/my/action1: got rejecting signal, do not continue');
        return Promise.reject({mute: true});
      }else{
        console.log('[INFO] /api/my/action1: no rejecting signal, continue');
        return ajxGet('/api/my/action2');
      }
    })
    .then(response2 => {
      if(response2.do_reject_other_actions){
        console.log('[INFO] /api/my/action2: got rejecting signal, do not continue');
        return Promise.reject({mute: true});
      }else{
        console.log('[INFO] /api/my/action2: no rejecting signal, continue');
        return ajxGet('/api/my/action3');
      }
    })
    .then(response3 => {
      console.log('[INFO] /api/my/action3: hurrah!! we got message: ', response3.message);
    })
    .catch((xhr) => {
      if(xhr && xhr.mute){
        console.log('[INFO] normal chain break.');
        return
      }
      console.info('[ERROR] Got error with message:', xhr.responseJSON.message);
    });
}

请注意,代替fail(...),您需要在链的末尾使用catch(...),因为fail是jQuery Deferred的方法。