#node.js #asynchronous #concurrency
#node.js #асинхронный #параллелизм
Вопрос:
Я пытаюсь вызвать несколько конечных точек с использованием NodeJS, и это дает результат, но проблема в том, что это дает результат после того, как все запросы получили 200 или какой-то допустимый HTTP-ответ. Что я хотел сделать, так это получить все результаты независимо от другого API. Например, если я вызываю 3 конечные точки, а именно. A, B, C. Если конечная точка B задерживает ответ из-за проблем с сервером, мой код не должен ждать, пока B завершит запрос и отправит ответ на другие завершенные запросы.
Я использую Async Promise, но он не выдает желаемый результат. Он печатает весь результат, когда выполняется запрос самого медленного API.
const request = require('request');
var requestAsync = function(url) {
return new Promise((resolve, reject) => {
var req = request(url, (err, response, body) => {
if (err) return reject(err, response, body);
resolve(JSON.parse(body));
});
});
};
const urls = [
'https://jsonplaceholder.typicode.com/posts',
'https://jsonplaceholder.typicode.com/albums',
'https://jsonplaceholder.typicode.com/users',
'http://localhost/local/node/1.php',
];
var getParallel = async function() {
//transform requests into Promises, await all
try {
var data = await Promise.all(urls.map(requestAsync));
} catch (err) {
console.error(err);
}
console.log(data);
}
getParallel();
Комментарии:
1. если вы заранее знаете длину массива, вы могли бы попробовать
// this calls in parallel var results = urls.map(requestAsync); var data1 = results[0].then(d => // do something).catch()
и т.д.. (Вы могли бы попробовать for loop, но никогда не пробовали)2. Я не думаю, что
await
это хорошо для того, чего вы пытаетесь достичь, если (к счастью) вы не делаетеawait call()
в качестве последнего вызова, вам придется подождать, пока await разрешится3. Звучит неплохо, я постараюсь вам перезвонить. Спасибо за быстрый ответ.
Ответ №1:
Используйте Promise.race
, чтобы гарантировать, что возвращенное обещание разрешится (или отклонится) не более чем через указанный период времени. Например, приведенное ниже гарантирует, что Promise.all
будет либо разрешено, либо отклонено через 3000 мс:
var requestAsync = function(url) {
return Promise.race([
new Promise(resolve => setTimeout(resolve, 3000, 'too slow')),
new Promise((resolve, reject) => {
var req = request(url, (err, response, body) => {
if (err) return reject(err, response, body);
resolve(JSON.parse(body));
});
})
]);
};
Если вы хотите отклонить, если какие-либо из них занимают слишком много времени, просто передайте reject
в setTimeout
вместо resolve
(где reject
является вторым параметром new Promise
обратного вызова).
Комментарии:
1. или
var data = await Promise.race(urls.map(requestAsync));
2. Однако это всегда будет ждать только одного результата.
3. оппс, извините за неправильное прочтение! не заметил
get all results irrespective of other API
4. Спасибо за решение, но после 3 секунд ожидания мой отложенный запрос вообще не дает мне никакого ответа. Он печатает слишком медленно, а затем останавливается, но печатается другой ответ. Я что-то здесь упускаю? Я поместил один из своих локальных api в список URL, который я отрегулировал и дает отложенный ответ через 30 секунд.
5. @GaneshSawant Я не могу воспроизвести вашу проблему — похоже, у меня все работает нормально: jsfiddle.net/9fgkd416
Ответ №2:
Я не думаю, что await
(или Promise.all
) подходит для того, чего вы пытаетесь достичь, если (к счастью) вы не выполняете await call () в качестве последнего вызова, вам придется подождать, пока await
разрешится (или все обещания будут разрешены или отклонены).
Допустим, у вас есть, giveSome()
который выполняет некоторую асинхронную работу и возвращает Promise
. Допустим, у вас есть задача A
и B
. A завершается через 5 секунд, а B — через 1 секунду. Теперь вы делаете
// called in parallel
// both a and b are promise pending
var a = giveSome() // task A
var b = giveSome() // task B
// do something with data in async function
console.log(await a) // this is resolved only after 5s
console.log(await b) // this is already resolved after 1s but waits for the await above
Вместо этого, если вы сделаете:
a.then(val => console.log(val)) // outputs val after 5s
b.then(val => console.log(val)) // outputs val after 1s
Итак, в вашем случае вы могли бы:
var data = urls.map(requestAsync);
for (let i = 0; i < data.length; i ) {
data[i]
.then(val => console.log(val) // do something)
// burden is you need to handle errors individually
.catch(e => console.error(e))
}
Ответ №3:
Допустим, вы хотите вызвать метод x
после каждого ответа. Вместо того, чтобы выполнять вызов Promise.all
по запросу, вы можете создать другое обещание для выполнения вашей работы. Оберните каждый запрос в отдельный, Promise
который выполнит вашу работу, а затем используйте другой, Promise.all
просто чтобы проверить, выполнено ли выполнение для всех. Вот пример кода —
let promises = [];
urls.forEach(url => {
promise.push(requestAsync(url)
.then(x) // Replace `x`
.catch(console.error) // Silently dropping error
)
})
Promise.all(promises)
.then((results) => {
console.log("All Executed", {results}); // Result will be response from `x` method
})
.catch(console.error); // If method `x` throws an error, it will go to this catch