Hello I'm new to NodeJs and am trying to work out the best way to get this chain of events working. I have to do two API calls get all the information I need. The first API call is just a list of IDs, then the second API call I pass the ID to get the rest of the information for each object.
However using the method below, I have no idea when everything is finished. Please can someone help me out.
function getData() {
var options = {
method: 'GET',
uri: '',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 1");
var obj = JSON.parse(apires);
obj.data.forEach(function(entry) {
findMore(entry.id)
});
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: '',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 2");
var obj = JSON.parse(apires);
})
}
Hello I'm new to NodeJs and am trying to work out the best way to get this chain of events working. I have to do two API calls get all the information I need. The first API call is just a list of IDs, then the second API call I pass the ID to get the rest of the information for each object.
However using the method below, I have no idea when everything is finished. Please can someone help me out.
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 1");
var obj = JSON.parse(apires);
obj.data.forEach(function(entry) {
findMore(entry.id)
});
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 2");
var obj = JSON.parse(apires);
})
}
Share
Improve this question
asked Aug 22, 2017 at 19:21
colouredFunkcolouredFunk
7881 gold badge15 silver badges37 bronze badges
1
-
in
findMore
returnrequest(options)
so you can chain the call tofindMore
with a.then
and handle the "all finished" there. Actually you will need to use Promise.all there, I just noticed theforEach
– martskins Commented Aug 22, 2017 at 19:29
5 Answers
Reset to default 3You can make your findMore method return a promise, so you can pass an array of those to Promise.all and handle the .then when all promises have finished.
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 1");
var obj = JSON.parse(apires);
var promises = [];
obj.data.forEach(function(entry) {
promises.push(findMore(entry.id));
});
return Promise.all(promises);
})
.then(function (response) {
// Here response is an array with all the responses
// from your calls to findMore
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.',
qs: {
access_token: _accessToken,
}
};
return request(options);
}
A couple of things to think about:
If you care about the fate of a promise, always return it.
In your case, findMore
does not return the promise from request
, so getData
has no handle to track the resolution (or rejection) of that promise.
You can track the resolution of multiple promises with Promise.all.
The Promise.all() method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.
Lets put these to use on your example:
function getData() {
var options = {
method: 'GET',
uri: 'https://api.call1.',
qs: {
access_token: _accessToken,
}
};
return request(options)
.then(function(apires){
var obj = JSON.parse(apires);
var findMorePromises = obj.data.map(function(entry) {
return findMore(entry.id)
});
return Promise.all(findMorePromises);
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.',
qs: {
access_token: _accessToken,
}
};
return request(options)
.then(function(apires){
return JSON.parse(apires);
})
}
I've used map to construct the array of promises, but you could just as well use a foreach
and push into an array similar to be more similar to your example code.
It's also good practice to make sure you are handling rejection of any promises (via catch
), but I'll assume that is out of the scope of this question.
You want to use Promise.all.
So first thing first, you need an array of promises. Inside your for each loop, set findMore to a variable, and make it return the promise. Then have a line where you do Promise.all(promiseArr).then(function(){console.log("done)})
Your code would look like this
function getData() {
var promiseArr = []
var options = {
method: 'GET',
uri: 'https://api.call1.',
qs: {
access_token: _accessToken,
}
};
request(options).then(function(apires){
console.log("plete 1");
var obj = JSON.parse(apires);
obj.data.forEach(function(entry) {
var p = findMore(entry.id)
promiseArr.push(p)
});
}).then(function(){
Promise.all(promiseArr).then(function(){
console.log("this is all done")
})
})
}
function findMore(id) {
var options = {
method: 'GET',
uri: 'https://api.call2.',
qs: {
access_token: _accessToken,
}
};
return request(options).then(function(apires){
console.log("plete 2");
var obj = JSON.parse(apires);
})
}
the basic idea of Promise.all is that it only executes once all promises in the array have been resolved, or when any of the promises fail. You can read more about it here
You need to use Promise.all
to run all async requests in parallel. Also you must return the result of findMore
and getData
(they are promises).
function getData() {
var options = {...};
return request(options)
.then(function(apires) {
console.log("plete 1");
var obj = JSON.parse(apires);
var ops = obj.data.map(function(entry) {
return findMore(entry.id);
});
return Promise.all(ops);
}
function findMore(id) {
var options = {...};
return request(options)
.then(function(apires) {
console.log("plete 2");
return JSON.parse(apires);
});
}
getData()
.then(data => console.log(data))
.catch(err => console.log(err));
If you can use ES7, it can be written with async/await:
let getData = async () => {
let options = {...};
let res = awit request(options);
let ops = res.data.map(entry => findMore(entry.id));
let data = await Promise.all(ops);
return data;
};
let findMore = async (id) => {
let options = {...};
let apires = awit request(options);
return JSON.parse(apires);
};
EDIT: As others have mentioned, using a Promise.all() is likely a better solution in this case.
If you are open to using jQuery (a JavaScript library), then you can use the .ajaxStop() event handler and specify your own function. Sample code:
$(document).ajaxStop(function(){
alert("All AJAX requests are pleted.");
});
You will need to include the jQuery module. The instructions for Node.js are:
Install module through npm:
npm install jquery
Then use a "require" to use jQuery in your JavaScript code (a window with a document is required but there is no such "window" in Node so you can mock one with jsdom), see npm - jQuery for details:
require("jsdom").env("", function(err, window) { if (err) { console.error(err); return; } var $ = require("jquery")(window); });
If you want to stick to a pure JavaScript approach, you will need to create your own "module" to keep track of AJAX requests. In this module you can keep track of how many pending requests there are and remove them once they are terminated. Please see: Check when all Ajax Requests are plete - Pure JavaScript for more details.