2017-06-20 2 views
1

Hier ist Node.js neu. Ich bin auf der Suche nach dem richtigen Weg, um N asynchrone API-Aufrufe von innerhalb einer anderen Funktion zu machen und ihre Ergebnisse zu kombinieren, um weiter stromabwärts zu verwenden. In meinem Fall wäre N relativ klein und blockiert für ihre Ausführung nicht zu schlecht.Kombinieren Sie die Ergebnisse mehrerer Node.js-API-Aufrufe

Bei synchroner Ausführung sollte die Implementierung für combine() unten funktionieren.

Wenn ich nur die Ergebnisse von einem API-Aufruf benötigt, wäre es einfach, die folgende Logik in einer Callback-Funktion zu implementieren, die an callAPI() übergeben wird. Wo ich stolpere ist, wenn ich alle Ergebnisse zusammen vor der Ausführung von foo brauche (total, [... args]).

Ich schaute in async.whilst, aber war nicht in der Lage, das zu arbeiten. Ich bin skeptisch, ob das tatsächlich zu meinen Bedürfnissen passt. Ich habe auch in Promises geschaut, das scheint die richtige Blei zu sein, aber es wäre nett, Zusicherungen zu bekommen, bevor man in dieses höhlenartige Kaninchenloch kriecht. Sei es, dass Promises der richtige Weg ist, welches Modul ist der Standard in Node.js Projekten zu verwenden?

var http = require('http'); 

function callAPI(id) { 
    var options = { 
     host: 'example.com', 
     path: '/q/result/'.concat(id) 
    } 

    var req = http.get(options, (res) => { 
     var body = []; 
     res.on('data', (chunk) => { 
      body.push(chunk); 
     }).on('end',() => { 
      body = Buffer.concat(body).toString(); 
      return body; 
     }).on('error', (err) => { 
      console.error(err); 
     }); 
    }); 
} 

function combine(inputs) { 

    var total = 0; 
    for (i=0; i < inputs.length; i++) { 
     total += callAPI(inputs[i]['id']); 
    }; 
    console.log(total); 

    // call some function, foo(total, [...args]) 
} 

Edit 1:

ich versuchte samanime Antwort unten und ändern Sie den API-Aufruf zu folgen, ein Versprechen zurückzukehren. Siehe:

function callAPI(id) { 
    return Promise((resolve, reject) => { 
     var options = { 
      host: 'example.com', 
      path: '/q/result/'.concat(id) 
     } 

     var req = http.get(options, (res) => { 
      var body = []; 
      res.on('data', (chunk) => { 
       body.push(chunk); 
      }).on('end',() => { 
       body = Buffer.concat(body).toString(); 
       resolve(body); 
      }).on('error', (err) => { 
       reject(err); 
      }); 
     }); 
    }); 
} 

function combine(inputs) { 

    var combined = []; 
    for (i=0; i < inputs.length; i++) { 
     total += callAPI(inputs[i]['id']); 
      .then(result => { 
       combined.push(result); 
      }); 
    }; 
    var total = combined.reduce((a, b) => a + b, 0); 
    console.log(total); 

    // call some function, foo(total, [...args]) 
} 

Das scheint mir auf halbem Weg dorthin zu gelangen. Wenn ich console.log(combined) innerhalb der then() Block kann ich die Liste mit Ergebnissen von den API-Aufrufe aufbauen. Allerdings kann ich immer noch nicht auf das vollständige combined am "Ende" der for Schleife zugreifen. Kann ich einen Callback an etwas anhängen, das ausgeführt wird, nachdem die vollständige Liste erstellt wurde? Gibt es einen besseren Weg?

Edit 2 (Meine Lösung - pro Patrick Roberts Vorschlag)

function callAPI(id) { 
    return Promise((resolve, reject) => { 
     var options = { 
      host: 'example.com', 
      path: '/q/result/'.concat(id) 
     } 

     var req = http.get(options, (res) => { 
      var body = []; 
      res.on('data', (chunk) => { 
       body.push(chunk); 
      }).on('end',() => { 
       body = parseInt(Buffer.concat(body)); 
       resolve(body); 
      }).on('error', (err) => { 
       reject(err); 
      }); 
     }); 
    }); 
} 

function combine(inputs) { 
    var combined = []; 
    Promise.all(inputs.map(input => callAPI(input.id))) 
     .then((combined) => { 
      var total = combined.reduce((a, b) => a + b, 0); 
      // foo(total, [...args]) 
     }); 
}; 
+0

versuchen, diese https: // Stackoverflow.com/questions/44636542/loop-through-asynchrone-Anfrage/44636736 # 44636736 Es ist genau das, was Sie suchen. –

Antwort

0

Ihre Bearbeitung ist viel besser aussehen, aber versuchen Sie dies:

function callAPI(id) { 
    return Promise((resolve, reject) => { 
    var options = { 
     host: 'example.com', 
     path: '/q/result/' + id 
    } 

    http.get(options, (res) => { 
     var body = []; 
     res.on('data', (chunk) => { 
     body.push(chunk); 
     }).on('end',() => { 
     body = Buffer.concat(body).toString(); 
     resolve(body); 
     }).on('error', reject); 
    }); 
    }); 
} 

function combine(inputs) { 
    Promise.all(inputs.map(input => callAPI(input.id))).then((combined) => { 
    // completed array of bodies 
    console.log(combined); 
    // foo(combined.length, [...args]); 
    }).catch((error) => { 
    console.log(error); 
    }); 
} 
2

Es klingt wie Sie nur ein paar Versprechungen Kette zusammen, entlang der Daten übergeben.

Im Grunde so etwas wie:

const combined = []; 
asyncOne() 
    .then(result => { combined.push(result); return asyncTwo()) 
    .then(result => { combined.push(result); return asyncThree()) 
    // and so on 

Solange jede Funktion ein Versprechen gibt, werden Sie alle eingestellt werden.

Wenn Sie wollen, dass sie parallel laufen zu lassen, verwenden Sie Promise.all(), was für Sie das gleiche tun:

Promise.all([asyncOne(), asyncTwo(), asyncThree() /* , etc */]) 
    .then(combined => /* combined is an array with the results of each */) 

Dies ist bei weitem das bevorzugte Muster für diese Art der Sache.

-1

Ich würde einen Zähler hinzufügen, der verbleibende API-Aufrufe verfolgt. Immer wenn ein API-Aufruf beendet ist, dekrementiere und wenn es 0 ist, bist du fertig.

const numCalls = 10; 
 
let remaining = numCalls; 
 
let data = []; 
 

 
function getRandomInt(min, max) { 
 
    min = Math.ceil(min); 
 
    max = Math.floor(max); 
 
    return Math.floor(Math.random() * (max - min)) + min; 
 
} 
 

 
function ajax() { 
 
    // Simulate ajax with a setTimeout for random amount of time. 
 
    setTimeout(() => { 
 
     // This is the callback when calling http.get 
 
     data.push(getRandomInt(0, 10)); // Some data from server 
 
     if (--remaining <= 0) { 
 
      // Am I the last call? Use data. 
 
      console.log(data); 
 
      console.log(data.length); 
 
     } 
 
    }, getRandomInt(1000, 3000)); 
 
} 
 

 
for (let i = 0; i < numCalls; i++) { 
 
    ajax(); 
 
}

+0

Es ist in der Regel eine schlechte Übung, Ihre Anwendungslogik mit einer asynchronen Kontrollflusslogik zu mischen. Aus diesem Grund wurden 'Promises' erstellt und vor ihnen Bibliotheken wie' async'. –

+0

Sicher. Alles hängt davon ab. Verwenden Sie Versprechen, wenn Sie können. Ich würde jedoch argumentieren, dass das Aufrufen/Ablehnen an bestimmten Punkten in Ihrem Code asynchrone Kontrollflusslogik ist (aber sauberer) wir mischen es immer noch mit der App. Logik. – Peheje

+0

Ich spreche über _counting_ interne Rückrufe und _conditional_ Ausführung von Consumer-Rückrufe innerhalb Ihrer Anwendungslogik. Natürlich müssen Sie 'resolve()', 'reject()' oder 'callback()' oder 'next()' je nach dem asynchronen Kontrollfluss, mit dem Sie sich entscheiden, aber es ist viel sauberer als erfinden Sie das Rad mit Ihrem eigenen Versuch der Kontrollflusslogik. –

Verwandte Themen