Добавление Promise в Promise.all () [дубликаты]

    

На этот вопрос уже есть ответ здесь:

    

У меня есть вызов API, который иногда возвращает постраничные ответы. Я хотел бы автоматически добавить их в свои обещания, чтобы получить обратный вызов после получения всех данных.

Это моя попытка. Я ожидаю, что новое обещание будет добавлено, а Promise.all будет разрешен, как только это будет сделано.

На самом деле происходит то, что Promise.all не ждет второго запроса. Я предполагаю, что Promise.all присоединяет «слушателей» при вызове.

Есть ли способ «переустановить» Promise.all ()?

function testCase (urls, callback) {
    var promises = [];
    $.each(urls, function (k, v) {
        promises.push(new Promise(function(resolve, reject) {
            $.get(v, function(response) {
                if (response.meta && response.meta.next) {
                    promises.push(new Promise(function (resolve, reject) {
                        $.get(v + '&offset=' + response.meta.next, function (response) {
                            resolve(response);
                        });
                    }));
                }
                resolve(response);
            }).fail(function(e) {reject(e)});
        }));
    });

    Promise.all(promises).then(function (data) {
        var response = {resource: []};
        $.each(data, function (i, v) {
            response.resource = response.resource.concat(v.resource);
        });
        callback(response);
    }).catch(function (e) {
        console.log(e);
    });
}   

Желаемый поток - что-то вроде:

  1. Создайте набор обещаний.
  2. Некоторые из обещаний порождают больше обещаний.
  3. После того, как все первоначальные и порожденные обещания разрешатся, перезвоните.
4 голоса | спросил Josiah 13 FebruaryEurope/MoscowbMon, 13 Feb 2017 20:42:46 +0300000000pmMon, 13 Feb 2017 20:42:46 +030017 2017, 20:42:46

2 ответа


0

Похоже, что общая цель:

  1. Для каждой записи в urls вызовите $.get и дождитесь его завершения.
    • Если он возвращает только ответ без "next", оставьте этот один ответ
    • Если он возвращает ответ со словом «next», мы также хотим запросить «next», а затем оставить оба из них.
  2. Вызвать обратный вызов с помощью response, когда вся работа будет выполнена.

Я бы изменил # 2, чтобы вы просто вернули обещание и решили его с помощью response.

Ключевым моментом в обещаниях является то, что then возвращает новое обещание, которое может быть разрешено с помощью то, что вы возвращаете из then (напрямую, если вы возвращаете неотменяемое значение, или косвенно, если вы возвращаете потомственное значение, подчиняя себя это возможно). Это означает, что если у вас есть источник обещаний (в данном случае $.get), вам почти никогда не нужно использовать new Promise; просто используйте обещания, которые вы создаете с помощью then. (И catch.)

Смотрите комментарии:

function testCase(urls) {
    // Return a promise that will be settled when the various `$.get` calls are
    // done.
    return Promise.all(urls.map(function(url) {
        // Return a promise for this `$.get`.
        return $.get(url)
            .then(function(response) {
                if (response.meta && response.meta.next) {
                    // This `$.get` has a "next", so return a promise waiting
                    // for the "next" which we ultimately resolve (via `return`)
                    // with an array with both the original response and the
                    // "next". Note that since we're returning a thenable, the
                    // promise created by `then` will slave itself to the
                    // thenable we return.
                    return $.get(url + "&offset=" + response.meta.next)
                        .then(function(nextResponse) {
                            return [response, nextResponse];
                        });
                } else {
                    // This `$.get` didn't have a "next", so resolve this promise
                    // directly (via `return`) with an array (to be consistent
                    // with the above) with just the one response in it. Since
                    // what we're returning isn't thenable, the promise `then`
                    // returns is resolved with it.
                    return [response];
                }
            });
    })).then(function(responses) {
        // `responses` is now an array of arrays, where some of those will be one
        // entry long, and others will be two (original response and next).
        // Flatten it, and return it, which will settle he overall promise with
        // the flattened array.
        var flat = [];
        responses.forEach(function(responseArray) {
            // Push all promises from `responseArray` into `flat`.
            flat.push.apply(flat, responseArray);
        });
        return flat;
    });
}

Обратите внимание, что мы никогда не используем там catch; мы откладываем обработку ошибок для вызывающей стороны.

Использование:

testCase(["url1", "url2", "etc."])
    .then(function(responses) {
        // Use `responses` here
    })
    .catch(function(error) {
        // Handle error here
    });

Функция testCase выглядит очень долго, но это только из-за комментариев. Вот это без них:

function testCase(urls) {
    return Promise.all(urls.map(function(url) {
        return $.get(url)
            .then(function(response) {
                if (response.meta && response.meta.next) {
                    return $.get(url + "&offset=" + response.meta.next)
                        .then(function(nextResponse) {
                            return [response, nextResponse];
                        });
                } else {
                    return [response];
                }
            });
    })).then(function(responses) {
        var flat = [];
        responses.forEach(function(responseArray) {
            flat.push.apply(flat, responseArray);
        });
        return flat;
    });
}

... и было бы еще более кратким, если бы мы использовали функции стрелок ES2015. : -)


В комментарии вы спросили:

  

Может ли этот дескриптор, если был следующий следующий? Нравится страница 3 результатов?

Мы можем сделать это, заключив эту логику в функцию, которую мы используем вместо $.get, который мы можем использовать рекурсивно:

function getToEnd(url, target, offset) {
    // If we don't have a target array to fill in yet, create it
    if (!target) {
        target = [];
    }
    return $.get(url + (offset ? "&offset=" + offset : ""))
        .then(function(response) {
            target.push(response);
            if (response.meta && response.meta.next) {
                // Keep going, recursively
                return getToEnd(url, target, response.meta.next);
            } else {
                // Done, return the target
                return target;
            }
        });
}

Тогда наш основной testCase проще:

function testCase(urls) {
    return Promise.all(urls.map(function(url) {
        return getToEnd(url);
    })).then(function(responses) {
        var flat = [];
        responses.forEach(function(responseArray) {
            flat.push.apply(flat, responseArray);
        });
        return flat;
    });
}
ответил T.J. Crowder 13 FebruaryEurope/MoscowbMon, 13 Feb 2017 21:07:14 +0300000000pmMon, 13 Feb 2017 21:07:14 +030017 2017, 21:07:14
0

Предполагая, что вы используете jQuery v3 +, вы можете использовать обещания, возвращаемые $.ajax, чтобы перейти к Promise.all()

Что вам не хватает, так это возвращать второй запрос в качестве обещания, а не пытаться отправить его в массив обещаний

Упрощенный пример

var promises = urls.map(function(url) {
  // return promise returned by `$.ajax`
  return $.get(url).then(function(response) {
    if (response.meta) {
      // return a new promise
      return $.get('special-data.json').then(function(innerResponse) {
        // return innerResponse to resolve promise chain
        return innerResponse;
      });

    } else {
      // or resolve with first response
      return response;
    }
  });

})

Promise.all(promises).then(function(data) {
  console.dir(data)
}).catch(function(e) {
  console.log(e);
});

ДЕМО

ответил charlietfl 13 FebruaryEurope/MoscowbMon, 13 Feb 2017 21:12:09 +0300000000pmMon, 13 Feb 2017 21:12:09 +030017 2017, 21:12:09

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132