2016-12-05 129 views
0

我很努力地在以下任务中发送多个AJAX调用。 API返回值有两个参数:userIdoffsetValue,并返回指定用户的最后10条消息,从指定的偏移量开始。如果偏移量大于用户的消息总数,则API会返回一个空字符串。承诺顺序运行嵌套承诺,并在第一次拒绝时解决

我写了一个函数,返回一个单独的承诺,获得10条消息指定的userIdoffsetValue

function getMessages(userId, offsetValue) { 
    return new Promise(function (resolve, reject) { 
     $.ajax(
     { 
      url: 'https://example.com/api.php', 
      type: 'POST', 
      data: { 
       action: 'get_messages', 
       offset: offsetValue, 
       user: userId 
      }, 
      success: function (response) { 
       if (response != '') { 
        resolve(response); 
       } else { 
        reject(response); 
       } 
      }, 
      error: function (response) { 
       reject(response); 
      } 
     }); 
    }); 
} 

我需要使用.all()多个userId运行并行任务,但是我不能,因为我事先不知道有多少信息是如何做的每个用户的每个userId(由每次10递增offsetValue)平行的子任务有,所以执行应停止时,第一个人的承诺被拒绝(即offsetValue是超过总消息数)。事情是这样的:

var messages = ''; 

getMessages('Alex', 0) 
    .then(function(result) { 
     messages += result; 

     getMessages('Alex', 10); 
      .then(function(result) { 
       messages += result; 

       getMessages('Alex', 20) 
       .... 
      }); 
    }); 

那么,有没有办法一个一个依次运行与迭代参数sequental承诺和解决整体连接的结果在第一次拒绝?

+0

您正在寻找递归。 – Bergi

+2

你的意思是*顺序*。承诺永远不会同步。 – Bergi

回答

5

首先,你想避免promise anti-pattern,当你不必要地将代码包装在一个新的承诺中时,$.ajax()已经返回一个你可以使用的承诺。要解决这个问题,你应该改为:

// retrieves block of messages starting with offsetValue 
// resolved response will be empty if there are no more messages 
function getMessages(userId, offsetValue) { 
    return $.ajax({ 
     url: 'https://example.com/api.php', 
     type: 'POST', 
     data: { 
      action: 'get_messages', 
      offset: offsetValue, 
      user: userId 
     } 
    }); 
} 

现在,您的主要问题。鉴于当您收到拒绝或空的回复时,如果您想停止请求新项目,并且您不知道有多少请求会提前发出,那么您几乎必须连续请求事件,并在您收到请求时停止请求下一个请求空响应或错误。 做到这一点的关键是通过返回来自.then()处理程序的新承诺,将顺序承诺链接在一起。

可以是这样做的:

function getAllMessagesForUser(userId) { 
    var offsetValue = 0; 
    var results = []; 

    function next() { 
     return getMessages(userId, offsetValue).then(function(response) { 
      // if response not empty, continue getting more messages 
      if (response !== '') { 
       // assumes API is returning 10 results at a time 
       offsetValue += 10; 
       results.push(response); 
       // chain next request promise onto prior promise 
       return next(); 
      } else { 
       // empty response means we're done retrieving messages 
       // so just return results accumulated so far 
       return results.join(""); 
      } 
     }); 
    } 
    return next(); 
} 

这将创建一个返回承诺的内部函数,每一次它得到一些信息,其链的新承诺到当初的诺言。因此,getAllMessagesForUser()返回一个单一的承诺,解决它检索或拒绝的所有消息的错误。

你会使用这样的:

getAllMessagesForUser('Bob').then(function(messages) { 
    // got all messages here 
}, function(err) { 
    // error here 
}); 

你可以并行处理多个用户(只要你确定你不是服务器过载或运行到一个限速问题)是这样的:

$.when.apply($, ['Bob', 'Alice', 'Ted'].map(function(item) { 
    return getAllMessagesForUser(item); 
})).then(function() { 
    // get results into a normal array 
    var results = Array.prototype.slice.call(arguments); 
}); 

PS Promise.all()$.when()要好得多(因为它需要一个数组并解析为一个数组),但由于已经有jQuery promise在这里涉及,我不知道你的浏览器兼容性要求,所以我坚持使用jQuery承诺管理而不是ES6标准承诺。

+0

+1。要切换到标准的承诺,请参阅[这里](http://stackoverflow.com/a/31327725/1048572)。 – Bergi