Почему мой массив Promises работает до вызова Promise.all()?
Я пытаюсь создать массив обещаний, а затем разрешить их с помощью Promise.all(). Я использую get, который возвращает обещание.
Мой код работает, но я не совсем понимаю, как. Вот:
const got = require('got');
const url = 'myUrl';
const params = ['param1', 'param2', 'param3'];
let promiseArray = [];
for (param of params) {
promiseArray.push(got(url + param));
}
// Inspect the promises
for (promise of promiseArray) {
console.log(JSON.stringify(promise));
// Output: promise: {"_pending":true,"_canceled":false,"_promise":{}}
}
Promise.all(promiseArray).then((results) => {
// Operate on results - works just fine
}).catch((e) => {
// Error handling logic
});
Что меня отталкивает, так это то, что Обещания помечаются как "ожидающие", когда я добавляю их в массив, что означает, что они уже запущены.
Я думаю, что они должны лежать неактивно в promiseArray
, а также Promise.all(promiseArray)
и запустит их, и разрешит.
Значит ли это, что я начинаю их дважды?
4 ответа
Вы не начинаете их дважды. Обещания запускаются, как только они созданы - или как только движок JS находит достаточно ресурсов для их запуска. Вы не можете контролировать, когда они начнутся.
Все Promise.all()
делает, это ждать, пока все они уладят (решить или отклонить). Promise.all()
не вмешивается и не влияет на порядок / время выполнения самого обещания.
Обещания не выполняются вообще. Они являются просто системой уведомлений для связи, когда асинхронные операции завершены.
Итак, как только вы запустите это:
promiseArray.push(got(url + param));
Ваша асинхронная операция внутри got()
уже запущен, и когда он закончится, он сообщит об этом через обещание.
Все Promise.all()
он контролирует все обещания и сообщает вам, когда первый отказывается или когда все они выполнены успешно. Он никак не "контролирует" асинхронные операции. Вместо этого вы запускаете асинхронные операции, и они возвращаются через обещания. Вы контролируете, когда вы запускаете асинхронные операции, а затем асинхронные операции запускаются сами по себе.
Если вы разбиваете свой код на части, вот что происходит в каждой части:
let promiseArray = [];
for (param of params) {
promiseArray.push(got(url + param));
}
Это звонки got()
много раз запускала какая-либо асинхронная операция в этой функции. got()
предположительно возвращает объект обещания, который затем помещается в ваш promiseArray
, Итак, на этом этапе все асинхронные операции уже запущены и выполняются самостоятельно.
// Inspect the promises
for (promise of promiseArray) {
console.log(JSON.stringify(promise));
// Output: promise: {"_pending":true,"_canceled":false,"_promise":{}}
}
Этот цикл просто просматривает все обещания, чтобы увидеть, может ли какое-либо из них быть уже разрешено, хотя не следует ожидать, что это произойдет, потому что их базовые асинхронные операции были только что начаты в предыдущем цикле.
Promise.all(promiseArray).then((results) => {
// Operate on results - works just fine
}).catch((e) => {
// Error handling logic
});
Затем с Promise.all()
вы просто просите отслеживать массив обещаний, чтобы он сообщал вам, когда есть отклоненное обещание или когда все они выполнены успешно.
Обещания "запускаются", когда они создаются, то есть функция, которая дает вам обещание, уже запустила (часто асинхронные) операции, которые в конечном итоге приведут к асинхронному результату. Например, если функция возвращает обещание для результата HTTP-запроса, она уже запустила этот HTTP-запрос при возврате вам объекта обещания.
Неважно, что вы делаете или не делаете с этим объектом обещания, с этой функцией (got
) уже создал функцию обратного вызова, которую он передал асинхронному API, такому как HTTP-запрос / ответ API. В этой функции обратного вызова (которую вы не видите, если вы не проверяете источник got
) обещание будет выполнено, как только оно будет вызвано этим API. В примере HTTP-запроса API вызывает этот конкретный обратный вызов с ответом HTTP, и упомянутая функция обратного вызова затем разрешает обещание.
Учитывая все это, немного странно думать об обещаниях как о вещах, которые "запускаются" или "запускаются". Они просто созданы в состоянии ожидания. Остальная вещь - ожидающий обратный вызов от некоторого API, который, как мы надеемся, произойдет, а затем изменит состояние объекта обещания, вызывая then
Обратные вызовы.
Обратите внимание, что при получении массива URL-адресов с помощью Promise.all возникли некоторые возможные проблемы:
- Если какой-либо из URL-адресов не может получить ваше разрешение, оно никогда не вызывается (так что один не выполняется, и ваша функция разрешения никогда не вызывается.
- Если ваш массив очень большой, вы будете загромождать сайт и вашу сеть запросами, возможно, вы захотите ограничить максимальное количество открытых запросов или запросов, сделанных за определенный период времени.
Первая проблема легко решается, вы обрабатываете неудачные запросы и добавляете их в результаты. В обработчике разрешения вы можете решить, что делать с ошибочными запросами:
const got = require('got');
const url = 'myUrl';
const params = ['param1', 'param2', 'param3'];
const Fail = function(details){this.details = details;};
Promise.all(
params.map(
param =>
got(url + param)
.then(
x=>x,//if resolved just pass along the value
reject=>new Fail([reject,url+param])
)
)
).then((results) => {
const successes = results.filter(result=>(result && result.constructor)!==Fail),
const failedItems = results.filter(result=>(result && result.constructor)===Fail);
}).catch((e) => {
// Error handling logic
});
Точка 2 немного сложнее, регулирование может быть выполнено с помощью этой вспомогательной функции и будет выглядеть примерно так:
... other code
const max5 = throttle(5);
Promise.all(
params.map(
param =>
max5(got)(url + param)
.then(
x=>x,//if resulved just pass along the value
reject=>new Fail([reject,url+param])
)
)
)