Асинхронный вызов нескольких API с использованием NodeJS независимо от результата каждого

#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