承诺按顺序运行嵌套的promises并在第一次拒绝时解析

Tao*_*que 0 javascript ajax jquery promise

我正在努力在以下任务中发送多个AJAX调用.API返回有两个参数:userIdoffsetValue从指定的偏移量开始,返回指定用户的最后10条消息.如果偏移量大于用户的消息总数,则API返回空字符串.

我编写了一个函数,它返回一个单独的promise,以获取指定userId和的10条消息offsetValue.

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);
            }
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

我需要使用.all()for multiple 执行并行任务userId,但是我不能为每个执行并行子任务(每次userId增加offsetValue10),因为我事先并不知道每个用户有多少消息,因此执行应该在第一个单独的承诺时停止被拒绝(即offsetValue超过总消息数).像这样的东西:

var messages = '';

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

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

                getMessages('Alex', 20)
                ....
            });
    });
Run Code Online (Sandbox Code Playgroud)

那么,有没有办法按顺序逐个迭代参数运行序列承诺并在第一次拒绝时解析整体连接结果?

jfr*_*d00 5

首先,你想要避免使用promise模式,当你$.ajax()已经返回一个你可以使用的promise时,你不必要地将你的代码包装在一个新的promise中.要解决这个问题,您可以更改为:

// 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
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

现在,针对您的主要问题.如果您想要在拒绝或空响应时停止请求新项目,并且您不知道预先会有多少请求,那么您几乎必须连续请求事项并在获得后请求下一个请求空响应或错误. 这样做的关键是通过从.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();
}
Run Code Online (Sandbox Code Playgroud)

这会创建一个返回promise的内部函数,每次获取一些消息时,它都会将新的promise链接到原始的promise上.因此,getAllMessagesForUser()返回一个单一的promise,它会解析它已检索到的所有消息,或者因错误而拒绝.

你会像这样使用它:

getAllMessagesForUser('Bob').then(function(messages) {
    // got all messages here
}, function(err) {
    // error here
});
Run Code Online (Sandbox Code Playgroud)

您可以并行化多个用户(只要您确定没有超载服务器或遇到速率限制问题),如下所示:

$.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);
});
Run Code Online (Sandbox Code Playgroud)

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