How to write callback function inside for loop in node.js - javascript

I am trying to write a function inside a for loop. But the loop is not waiting until getting response for the function inside. How can I stop the loop until getting response for the function every time?
In this process the next loop is dependent on the response of the function.
My example code:
var a = function(data, callback) {
var d = 1;
for (var i = 0; i < data.length; i++) {
b(d, function(err, result) {
if (!err) {
d = result;
}
if ((i + 1) === data.length) {
callback(err, 'something');
}
});
}
}
var b = function(data, callback) {
var c = data + 1;
callback(null, c);
}
In this code the for loop is not waiting until it gets the response form the function b.

You can easily iterate your input array by calling a recursive function call within your callbacks:
'use strict';
const a = function(data, callback) {
let i = 0;
let sum = 0;
// loop function
function next() {
const x = data[i++];
if (!x) {
return callback(null, sum);
}
return b(x, (err, data) => {
sum += data;
next();
});
}
next(); // starts iterating
}
// multiplies data * 3
const b = function(data, callback) {
let c = data * 3;
callback(null, c);
}
a([1, 2, 3], (err, data) => {
console.log(data); // prints 18
});

Use Promises
Your code can easily be refactored to use Promises:
function a (data) {
let promise = Promise.resolve(1);
for (let i = 0; i < data.length; i++) {
promise = promise.then(b);
}
return promise.then(function (v) {
// v is the value computed by the promise chain
return 'something';
});
}
function b (data) {
// some async action that returns a promise
return Promise.resolve(data + 1);
}
Usage:
a(['a', 'b', 'c']).then(result => console.log(result)); // logs 'something'
Use async functions
Working with promises allows you to treat asynchronous code like synchronous code using async functions:
async function a (data) {
let v = 1;
for (let i = 0; i < data.length; i++) {
// looks like synchronous code, but is asynchronous
v = await b(v);
}
// v is the value computed by the asynchronous loop
return 'something';
}

Related

merge callback function result with call limit

Disclaimer: This was an interview question I was asked today. However, I do think there are real world use cases.
Supposedly, we have a function called helperLimit, it takes in 4 params. This function will merge list of results from multiple callback functions.
args: an array of argument (IDs)
limit: number of callback function can be fire at any given time
fn: a callback function that takes arg (ID) and a callback as param
done: a callback merges all async results from list of calls using each arg (ID)
I can come up with partial answer but I don't know how to handle the rate limit control portion of the question.
// This function works except the limit part
function helperLimit(args, limit, fn, done) {
const final = [];
let complete = args.length;
for (let currentIndex = 0; currentIndex < args.length; currentIndex++) {
fn(args[currentIndex], (result) => {
final[currentIndex] = result;
complete--;
if (complete === 0) {
done(final)
}
})
}
}
// simulate API calls
function getById(id, callback) {
const random = Math.floor(Math.random() * 1000) + 5;
setTimeout(() => {
callback({id, result: `result ${id}`});
}, random)
}
helperLimit([1,2,3,4,5], 2, getById, (result) => { console.log(result); })
Thank you in advance for providing helps.
To implement the limit feature, call the function at most limit times in the loop. Then in the callback that you have for fn, call fn again (once) until you have covered all calls:
function helperLimit(args, limit, fn, done) {
const final = [];
let stillToComplete = args.length;
limit = Math.min(limit, args.length);
let currentIndex = 0;
while (currentIndex < limit) perform(currentIndex++);
function perform(index) {
fn(args[index], (result) => {
final[index] = result;
stillToComplete--;
if (stillToComplete) {
if (currentIndex < args.length) perform(currentIndex++);
} else {
done(final);
}
})
}
}

How to wait for iteration to complete before returning

I am trying to loop through an Array of JSON objects (var requestArray = req.body;, specifically requestArray['filter']), persisting each object into a database. After each persistence, I pull the last persisted data table, add it to an array let responseDataArray = []; in responseDataArray.push(result);. This array is then returned as a request response.
app.post('/sound', function (req, res) {
var requestArray = req.body;
let responseDataArray = [];
for (var i = 0; i < requestArray['filter'].length; i++) {
if (i > 3)
break;
var revEl = requestArray['filter'][i];
// console.log('GUID >>> ' + i + ' : ' + revEl['_revEntityGUID'] + ' >>> ' + JSON.stringify(revEl));
persistSingleItemPromise(revEl).then(function (result) {
responseDataArray.push(result);
console.log(JSON.stringify(responseDataArray));
});
}
console.log((responseDataArray));
res.send(responseDataArray);
});
The problem is in the for loop. It delays, and I only return an empty array responseDataArray = [] since it returns before the iteration completes.
I have tried using a Promose persistSingleItemPromise:
let persistSingleItemPromise = function (revData) {
return new Promise(function (resolve, reject) {
revPersSaveRevEntity.revPersSaveRevEntity(revData, function (result) {
resolve(result);
});
});
};
This doesn't help. How can I resolve this?
Thank you all in advance.
I was thinking of something like this.
Didn't test it please let me know if it works ;-)
Keep in mind, that your callback needs the async prefix too.
const resultPromise = requestArray['filter'].reduce( async ( accPromise, revEl ) => {
const acc = await accPromise
const result = await persistSingleItemPromise(revEl)
acc.push( result )
return result
}, Promise.resolve( [] ) )
const responseDataArray = await resultPromise
You could use Promise.all and store the promises. Then, wait for all of them to resolve
Like
app.post("/sound", function(req, res) {
var requestArray = req.body;
let responsePromises = [];
for (var i = 0; i < requestArray["filter"].length; i++) {
if (i > 3) break;
var revEl = requestArray["filter"][i];
// console.log('GUID >>> ' + i + ' : ' + revEl['_revEntityGUID'] + ' >>> ' + JSON.stringify(revEl));
responsePromises.push(persistSingleItemPromise(revEl));
}
Promise.all(responsePromises).then(result => res.send(result));
});
An example simulation here
const promises = [];
for (let i = 1; i < 4; i++) {
promises.push(new Promise(resolve => {
// Simulate asynchronous request
setTimeout(() => {
resolve("Resolved " + i);
}, 100 * i);
}));
}
// Notice how the result takes some time.
// It's basically waiting for all the promises to resolve
Promise.all(promises).then(results => console.log(results));
I think you should add all your promises from "persistSingleItemPromise" to an array and perform a Promise.All(list).then() on them and await the result before returning.

How to retrieve value from promise [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I've been working on a project and I need some help regarding this:
I have a promise inside a function and I need to return the value of "current" so that I can use it elsewhere.
I'm retrieving data from Firebase, then I shuffle the result and extract only 10 elements from the result.
function retriveData() {
//declaration of variables ...
axios.get("Firebase link")
.then((response) => {
keyArray = Object.keys(response.data);
let k = shuffle(keyArray);
//shuffle is a function to shuffle the key results
for (var i = 0; i < 10; ++i) {
current[i] = response.data[k[i]];
}
});
return current;} //I want this variable to end up with the promise result in current
I know, this is not how promises work but I need a solution to solve this problem.
Thanks!
axios.get is asynchronous, so either you pass a callback to retrieveData, or it needs to return a Promise itself. There's no way around that.
Using a callback (no error handling):
function retriveData(callback) {
axios.get("Firebase link")
.then((response) => {
keyArray = Object.keys(response.data);
let k = shuffle(keyArray);
//shuffle is a function to shuffle the key results
for (var i = 0; i < 10; ++i) {
current[i] = response.data[k[i]];
}
callback(null, current);
});
}
retrieveData((err, result) => console.log(result));
Using a Promise (no error handling):
function retriveData() {
return new Promise((resolve) => {
axios.get("Firebase link")
.then((response) => {
keyArray = Object.keys(response.data);
let k = shuffle(keyArray);
//shuffle is a function to shuffle the key results
for (var i = 0; i < 10; ++i) {
current[i] = response.data[k[i]];
}
resolve(current);
});
}
retrieveData().then((result) => console.log(result));
[EDIT] The above example is mean for illustrative purposes. Since axios.get already returns a Promise, it can be returned back directly from retrieveData.
function retriveData() {
return axios.get("Firebase link")
.then((response) => {
keyArray = Object.keys(response.data);
let k = shuffle(keyArray);
//shuffle is a function to shuffle the key results
for (var i = 0; i < 10; ++i) {
current[i] = response.data[k[i]];
}
return current;
});
}
retrieveData().then((result) => console.log(result));
Try this: I am making your retriveData function as a promise so you can use it anywhere in your program
function retriveData() {
//declaration of variables ...
return new Promise((resolve, reject) => {
axios.get("Firebase link")
.then((response) => {
keyArray = Object.keys(response.data);
let k = shuffle(keyArray);
//shuffle is a function to shuffle the key results
for (var i = 0; i < 10; ++i) {
current[i] = response.data[k[i]];
}
// if everything fine, if you get any conditional error then call reject();
resolve(current);
});
})
}
//call the function like promise
retriveData().then(result => {
//current will come here
console.log('result comes here');
}).catch(error => {
//error comes here (reject error)
console.log('error');
})

Resolving Promise.all with a promise as part of array or object

I am trying to queue a bunch of asynchronous calls to fire in parallel. However, the promises I am queuing have additional data that I want to keep along with the promise value.
My question is: how can I pass an object or array, which contains a promise, and have the promises resolve to a value within the object?
For example, let’s generate a normal array of promises:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asyncFunction(i));
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
This is working just fine. But now if we try to place the promise into an object, with some metadata:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
The value in value is a promise, and not the resolved value.
My expected output is:
[{"id":0,"value":0},{"id":1,"value":10},{"id":2,"value":20},...]
You can push promises that resolve to the format you want:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(
asyncFunction(i).then(value => ({
id: i,
value,
}))
);
}
let result = await Promise.all(promises);
console.log(result);
}
main();
You could map over the array of promises and await the value key containing the promise to resolve
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises.map(async (promise) => ({
id: promise.id,
value: await promise.value
})));
document.getElementById('output').innerText = JSON.stringify(result);
}
main();

How to chain promises in for loop in vanilla javascript

If I am doing an async call like the following, how can chain them with promises, so i can do stuff in order? In this example, what ends up happening is that arr will push the items out of order. I'd prefer an answer with promises, but anything will do as long as it works
var fbArrOfAlbumNames = ['Profile Pictures', 'Cover Photos', 'Mobile Uploads'];
var arr = [];
for(var x = 0; x < fbArrOfAlbumNames.length; x++) {
(function(cntr) {
FB.api('/' + fbArrOfAlbumNames[cntr] + '/photos/', {fields: 'picture,album'}, function(response) {
arr.push(response);
}
})(x);
}
Assuming your ajax calls can actually be run in parallel and you just want the results in order, then you can promisify the ajax function and use Promise.all() to get all the results in order:
// promisify the ajax call
function fbAPIPromise(path, args) {
return new Promise(function(resolve, reject) {
FB.api(path, args, function(results) {
if (!result) return resolve(null);
if (result.error) return reject(result.error);
resolve(result);
});
});
}
var promises = [];
for (var x = 0; x < 10; x++) {
promises.push(fbAPIPromise('/' + fbArrOfAlbumNames[x] + '/photos/', {fields: 'picture,album'});
}
Promise.all(promises).then(function(results) {
// results is an array of results in original order
}).catch(function(err) {
// an error occurred
});

Categories

Resources