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
});
Related
I'm trying to resolve a promise inside a for-loop in node js. In my code, I have a for loop in which I call a function findincollection that returns a promise. Then I push the data to finalresult array and resolve it once the for loop completes. But the issue I'm facing is it doesn't resolve the complete data. The for loop execution is getting finished before all the promises are resolved.
The line console.log(p1); always prints Promise { } but it eventually gets resolved as you can see in my code in the p1.then() statement, I do get the data one by one. But the finalresult array resolves too early. I also want to know why I always get Promise { } even when the promises are still getting resolved eventually.
Please have a look at my code below :
var mob = [123, 456, 789];
var list = [1, 2, 3, 4, 5, 6, 7, 8];
var res = [];
var finalresult = [];
for (y = 0; y < list.length; y++) {
const p1 = findincollection(list[y], mob, savetofile);
console.log(p1); //always prints Promise { <pending> } 8 times
p1.then(function(dt) {
finalresult.push(dt); //pushes all 3 objects one by one
console.log(dt); //prints 3 objects one by one
client.close();
if (y == (collist.length)) { //check if the loop has reached the last index
resolve(finalresult); //resolves the finalresult array with 1 object only instead of 3. I want this part to resolve the complete finalresult array i.e with all 3 objects.
}
});
}
const findincollection = function(index, mob, save) {
return new Promise((resolve, reject) => {
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
const db = client.db(dbName);
const collection = db.collection(col);
collection.find({ 'Numbers': { $in: mob } }).toArray(function(err, docs) {
const c = save(index, docs);
c.then(function(m) {
console.log(m); //print's Saved 3 times as the mob array length is 3
client.close();
return resolve(res);
})
});
});
});
}
const save = function(index, data) {
return new Promise((resolve, reject) => {
if (data.length > 0) {
for (var k = 0; k < data.length; k++) {
res.push(data[k]);
}
fs.appendFile('textlogs/' + index + '.txt', data, function(err) {
if (err) throw err;
resolve('Saved');
});
}
});
}
I'm not able to figure out how to make the loop wait until all the promises are resolved or make the code synchronous and then only resolve the finalresult array? How do I do it?
What you need here is Promise.all(). It returns a new promise, which gets resolved once all passed promises get resolved.
You can try something similar to:
var promises = [];
for (y = 0; y < list.length; y++) {
promises.push(findincollection(list[y], mob, savetofile));
}
Promise.all(promises)
.then(dt => {
finalresult.push(dt); //pushes all 3 objects one by one
console.log(dt); //prints 3 objects one by one
client.close();
if (y == (collist.length)) {
resolve(finalresult);
}
}); // <-- this will be raised once all your promises are resolved
I see you're closing client inside each promise and this client is not a local variable. So I think you should be closing it after all your promises are completed.
I am writing a NodeJS app that will load data from a database, parse it, and then save the parsed result to a different table in the database. Here is what I currently have:
parse(index, from, to) {
var collection = this.getCollectionName();
var interval = global.Settings.Parser.ParseInterval;
var promises = [];
console.log('%d - %d', from, from + interval);
for(from; from < to; from += interval) {
promises.push(new Promise((resolve, reject) => {
var scoped = from;
this.data.query(collection, {[index]: { $gte: from, $lte: from + interval}, (result) => {
for(var i = 0; i < result.length; i++)
this.sendToBuilder(result[i]);
resolve();
});
}));
}
promises.reduce((promise) => {
Promise.resolve()
});
}
The code seems to do what it should, but since the database query is asynchronous, it seems that out-of-order is a common occurrence. I do not want this to happen. I want each query and promise to execute sequentially to maintain the order of data.
I am trying the array.reduce() method to try and chain each promise to execute sequentially, but due to the nature of Promises it simply starts the promise and continues on, making them all fire simultaneously.
How can I ensure that it will execute sequentially? I don't mind delays between each promise as long as it doesn't block the actual thread.
Here's a working example in the spirit of your code using async/await:
function getData(reqId, collection, index, gte, lte) {
return new Promise((resolve, reject) => {
const delay = Math.floor(Math.random() * 2000) + 1;
console.log(`[getData ${reqId}] delay: ${delay}`);
const params = { collection, [index]: { $gte: gte, $lte: lte } };
const results = [0, 1, 2].map(r => `result ${reqId}.${r}`);
setTimeout(() => {
console.log(`[getData ${reqId}] this.data.query(${JSON.stringify(params)})`);
resolve(results);
}, delay);
});
}
async function parse(index, from, to) {
const collection = 'My Collection';
const interval = 10;
console.log(`Processing ${from} to ${to} by ${interval}:`);
for (from; from <= to; from += interval) {
const reqId = from;
console.log(`[parse] BEGIN ${reqId}`);
const results = await getData(reqId, collection, index, from, from + interval);
results.forEach(result => {
console.log(`[parse - awaited ${reqId}] this.sendToBuilder(${result})`);
});
console.log(`[parse] END ${reqId}`);
}
}
parse('idx', 200, 250);
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';
}
I'm new in promises. Im not sure how to write this code in bluebird promises. Can someone explain how this should look with promises?
for (var i = 10; i >= 0; i--) {
var path = '127.0.0.1/getdata/' + i;
http.get({
path: path,
}, (res) => {
var data = res.resume();
});
}
Thanks for everything
If you just want to collect the results of 10 requests, that don't depend upon one another, then you can launch them all in parallel and let promises collect the results for you. Since you mentioned Bluebird, you can let Bluebird make promisified versions of the http methods:
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'), {multiArgs: true});
var promises = [];
for (var i = 10; i >= 0; i--) {
var path = '127.0.0.1/getdata/' + i;
promises.push(request.getAsync(path).spread(function(response, body) {
return body;
}));
}
Promise.all(promises).then(function(results) {
// array of results here
}, function(err) {
// error here
});
function makeRequest() {
var items = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
return Promise.map(items, function(item) {
console.log(item);
return get('/echo/html/');
});
}
makeRequest().then(function(results) {
console.log(results);
});
fiddle
This approach is using Bluebird's Promise.map
I need to go through an array of values, look up date in Redis (to see if it exists), and then continue. For example:
var to_check = [ 1, 2, 3 ]
var found_elements = []
for (var i = 0; i < to_check.length; i++) {
redis.EXISTS('namespace:' + to_check.length[i], function(err, value) {
if (!err && value) {
found_elements.push(to_check.length[i])
}
})
}
console.log(found_elements.join(', '))
I need to get the last line executed after all callbacks sent to Redis have been executed. What would be the best way to approach this?
Use Promise to handle complex async operations. Parallel execution is one of them.
var to_check = [ 1, 2, 3 ];
var found_elements = [];
Promise.all(to_check.map(function(item){
return new Promise(function(resolve,reject){
redis.EXISTS('namespace:' + item, function(err, value) {
if(err){
return reject(err);
}
if (value) {
found_elements.push(item);
}
resolve();
})
});
})).then(function(){
console.log('All operations are done');
}).catch(function(err){
console.log(err);
});
I am sure there are other ways. But this should work(not tested):
var to_check = [ 1, 2, 3 ]
var found_elements = []
for (var i = 0; i < to_check.length; i++) {
(function(i){
redis.EXISTS('namespace:' + to_check.length[i], function(err, value) {
if (!err && value) {
found_elements.push(to_check.length[i])
}
if(i == (to_check.length-1)){
console.log(found_elements.join(', '))
}
})
})(i);
}