Async promises inside loop - javascript

I know this is a topic where there is a lot of info, but I still can't solve this.
I am working with NodeJs that connects to a mysql server, I have an array, I need to iterate the array and do some stuff with the data, the thing is I need to do a query to the mysql server but I need to the for loop wait until I got the results of the query, I have tried a lot of things, now I am trying with .each method of bluebird but still is not working properly, this is my code.
the initial function is Notification.getByUser
thanks in advance
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
if (result.length === 0) {
resolve(false);
} else {
resolve(true);
}
});
});
};
Notifications.isMatching = function(item, user, type) {
return new Promise((resolve, reject) => {
console.log(type, item, user);
if (item !== null) {
if (item !== user) {
resolve(false);
}
}
resolve(true);
});
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState, cb) {
var where = { status: 1 };
var plainText = '';
var items = Notifications.find({ where });
Promise.each(items, item => {
return Notifications.isMatching(item.isZolver, isZolver, 'isZolver')
.then(() => {
Notifications.isMatching(item.cityId, cityId, 'cityId');
})
.then(() => {
if(item.extraCondition !== null && item.extraCondition !== '') {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// console.log(sql);
Notifications.execute(sql, item.id)
.then(render => console.log('extraCondition', render));
} else {
console.log('extraCondition', true);
}
});
}).then(res => {
// console.log('res del loop', res);
});
cb(null, 'ok');
};
};

There are several issues with your code:
In order to chain promises, you must make sure to return promises that you create within a then callback
You should not need to create a promise for results that are immediately available (isMatching)
The then callback is always executed when a promise fulfils. It does not matter whether you do resolve(false): even if the promised value is false this will still make the promise fulfilled and trigger the then callback(s).
There are some unknowns in your code, like Notifications.find, and what kind of SQL you are executing: does it return a result set, if so, would you not be interested to get that result instead of just resolving to a boolean?
Anyway, here are some applied corrections:
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
resolve (result.length !== 0); // The `if ... else` is overkill
});
});
};
//You don't need isMatching to return a promise
Notifications.isMatching = function(a, b) {
return a === null || a === b;
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState) {
var where = { status: 1 };
var items = Notifications.find({ where })
// use filter() to limit the items to those you want to execute the SQL for
.filter(item => {
return Notifications.isMatching(item.isZolver, isZolver)
&& Notifications.isMatching(item.cityId, cityId)
&& item.extraCondition !== ''
});
// Return the promise! (don't go back to the old callback system!)
return Promise.each(items, item => {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// Return the promise!
return Notifications.execute(sql, item.id);
}).then(res => {
console.log('res del loop', res);
});
};
};
Note that the signature of Notifications.getByUser does not have the final callback parameter: you should keep using promises once you start with them, so when you call this function, call the result's then method like you would do for any promise:
Notifications.getByUser( .......arguments....).then( function () {
// do something...
});

Related

Detect if function was called with "then" [duplicate]

I often see in other peoples docs something like:
Callback is optional, if omitted returns a promise.
This is what I have:
export function doSomeAsync(options, callback) {
const useCallback = (callback && typeof callback == 'function');
const promise = new Promise((resolve, reject) => {
// --- do async stuff here ---
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
finish(true, "Number is 1");
} else {
finish(false, new Error("Number is not 1"));
}
}, 1000);
// ---------------------------
function finish(ok, rtn) {
if (useCallback) {
if (ok) {
callback(null, rtn);
} else {
callback(rtn, null);
}
} else {
if (ok) {
resolve(rtn);
} else {
reject(rtn);
}
}
}
});
return (useCallback) ? false : promise;
}
The finish() function just avoids lots of if... statements scattered around.
I'm creating a promise object, whether or not I use it.
Testing like this:
doSomeAsync({ num: 1 }).then((result) => {
console.log('p result', result);
}).catch((err) => {
console.log('p err', err);
});
doSomeAsync({ num: 1 }, (err, result) => {
if (err) {
console.log('cb err', err);
} else {
console.log('cb result', result);
}
});
This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?
This could be simplified if you simply always used the promise, which you're always creating anyway:
export function doSomeAsync(options, callback) {
const promise = new Promise((resolve, reject) => {
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
resolve("Number is 1");
} else {
reject(new Error("Number is not 1"));
}
}, 1000);
});
if (callback && typeof callback == 'function') {
promise.then(callback.bind(null, null), callback);
}
return promise;
}
Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.
You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:
const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
(resolve, reject) => doStuffWith(stuff, resolve, reject)
)
.then(response => callback(null, response))
.catch(callback);

Promises when passing a function as a parameter

I understand how promises work for the most part, but I have a lot of trouble understanding how to deal with them when I need to pass a function as a parameter:
var promise = new Promise(function(resolve, reject) {
// Do async job
ec2.describeInstances(function(err, data) {
console.log("\nIn describe instances:\n");
var list = [];
if (err) reject(err); // an error occurred
else {
var i = 0 ;
//console.log(data.Reservations);
var reservations = data.Reservations;
for (var i in reservations) {
var instances = reservations[i]['Instances'];
var j = 0;
//console.log(JSON.stringify(instances, null, 2));
for (j in instances){
var tags = instances[j]
var k = 0;
var instanceId = tags['InstanceId'];
var tag = tags['Tags'];
var l;
//console.log(tag);
for (l in tag){
//console.log(instanceId);
//console.log(tag[l]['Value']);
if (String(tag[l]['Value']) == '2018-10-15T23:45' || String(tag[l]['Key']) == 'killdate') {
console.log(tag[l]['Key'] + ' ' + tag[l]['Value']);
list.push(instanceId);
console.log(list);
//return(list);
}
}
}
}
resolve(list);
}
});
});
promise.then(function (list) {
ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
});
before I had the first part of the code as:
return new Promise(function(resolve, reject) { ... }
and that worked for the first part, but as soon as I changed it to a "var" and added the new promise in underneath, it stopped working. (edit) When I mean "stopped working" I mean, neither of the two functions run, i.e.: it ends the handler before either functions are finished and none of the return statements or console logs.
Any help would be greatly appreciated!
Thanks!
Wondering if something like this would work:
var promise = Promise.resolve(function() {
return ec2.describeInstances...
})
promise
.then(/* handle successful promise resolution */ )
.catch(/* handle promise rejection */ )
var promise = Promise.resolve();
promise
.then(function() {
return ec2.describeInstances(function(err, data) {
var list = [];
if (err) throw err; // an error occurred
// else logic
})
})
.catch(/* if needed here */)
.then(function (list) {
return ec2.terminateInstances(list, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log("made it"); });
})
.catch(/* if needed here */)
my suggestion is to break up your logic - it will be easier to handle the result you want to achieve.
A proper way in my opinion:
promise function(a service function):
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
result = () => resolve(data);
fail = () => reject(err);
});
}
then your promise caller:
myAsyncFunction().then(dataHandler(result), // "promise worked!"
function (err) {// Error: "It broke"
console.log(err)
});
then the logic:
function dataHandler(data) { /* data logic */}
good luck
I ended up fixing it. Sorry, forgot to post back before I added in the SNS portion. I ended up learning a ton about functional programming on the way and decided to use the await function over the complicated promise syntax. Below is the code:
exports.handler = async (event, result, callback) => {
const AWS = require('aws-sdk');
const date = new Date().toISOString().substr(0, 16)
const ec2 = new AWS.EC2();
var sns = new AWS.SNS();
console.log("date is: " + date)
console.log(date.length);
const params = {
TopicArn:'arn:aws:sns:us-east-1:503782973686:test',
Message:'Success!!! ',
Subject: 'TestSNS'
}
const describeResult = await ec2.describeInstances().promise()
const terminatableInstances = await describeResult
.Reservations
.reduce((acc, reservation) => acc.concat(reservation.Instances), [])
//'2018-10-15T23:45'
.map((instance) => {
//console.log(instance)
//console.log(instance.Tags)
var date = instance.Tags
.filter(tag => tag.Key == 'killdate' && tag.Value == date) //date should be in this format on tag: 2018-10-15T23:45
.reduce((acc, curr) => curr.Value, null);
if (date != null) {
return instance.InstanceId;
}
return null;
})
.filter(id => id != null)
console.log(terminatableInstances);
const snsPush = await ec2.terminateInstances({
InstanceIds: terminatableInstances,
//DryRun: true //set this flag if you want to do a dry run without terming instances
}, (err, data) => {
if (err) {
console.log('no instances to terminate!')
}
else {
console.log('terminated instances')
}
})
console.log(snsPush)
//return(snsPush).promise()
return sns.publish(params, (err, data) => {
if (err) {
console.log(err, err.stack);
}
else {
console.log('sent');
}
}).promise();
};

Promise return multiple values

I use the following code to return promise which is working OK.
The promise return the data value
run: () => {
return new Promise((resolve, reject) => {
....
}).then((data) => {
let loginApi = data[0]
let test = 1;
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
resolve(data )
}
})
});
I call it
module.run()
.then((data) => {
And I was able to get the data.
now I want to return also value test in the resolve, how should I do it?
I try to add it like this
resolve({data,test});
resolve([data,test]);
with call like
module.run()
.then({data,test}) => {
without success(test is empty), I read about spread but this is the only option?
I use ES6 with bluebird latest version
If you are using promise chain, in promise chain you have then->then->catch->... format. Always return Promise.resolve or Promise.reject. Promise.resolve will give success result for next then block and Promise.reject will go to next catch block.
var module = {
run: () => {
return new Promise((resolve, reject) => {
// ....
resolve('promise resolved')
}).then((data) => {
let loginApi = data[0]
let test = 1;
return Promise.resolve({data,test})
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
return Promise.resolve({data, test});
}
return Promise.reject(err);
})
}
};
module.run().then(({data, test}) => {
console.log(data, test);
})

Promise chains and anonymous promise returns

Here I have a chain of promises that works fine. All the *.destroy's are promises that return promises:
function callDBDestroy() {
var db;
DB_Categories.destroy().then(function () {
return DB_Equipment.destroy();
}).catch(function (err) {
showMsg("Error in callDBDestroy: " + err);
}).then(function () {
return DB_Certificates.destroy();
}).catch(function (err) {
showMsg("Error in callDBDestroy: " + err);
}).then(function () {
return DB_Locations.destroy();
}).catch(function (err) {
showMsg("Error in callDBDestroy: " + err);
});
}
But I want to add an if statement into each one to check to see if the PouchDB database exists (which it doesn't if the DB_* is null).
If it exists, I want to destroy it then return (and these all return promises).
If it doesn't exist, I want to return an anonymous promise which returns nothing as none of the promises have any data I am concerned with.
In the example, I added in some sample code to do the if statement and I was wondering what I would put in the null instance that would pass a promise (resolve) value.
function callDBDestroy() {
var db;
DB_Categories.destroy().then(function () {
if( DB_Equipment != null) {
return DB_Equipment.destroy();
}
else {
Anonymous empty promise - something like:
new Promise().resolve();
}
}).then(function () {
return DB_Certificates.destroy();
}).then(function () {
return DB_Locations.destroy();
}).catch(function (err) {
showMsg("Error in callDBDestroy: " + err);
});
}
Thanks,
Tom
It looks like you are just wondering how to manually resolve/reject a Promise. If that is the case you can just call Promise.resolve(optionalValue) or Promise.reject(optionalValue) if you want to go to the catch handler:
function callDBDestroy() {
var db;
DB_Categories.destroy()
.then(function () {
if( DB_Equipment != null) {
return DB_Equipment.destroy();
} else {
return Promise.resolve();
}
}).then(function () {
return DB_Certificates.destroy();
}).then(function () {
return DB_Locations.destroy();
}).catch(function (err) {
showMsg("Error in callDBDestroy: " + err);
});
}
You could wrap it:
function withDb(db, handler) {
return function onFulfilled(value) {
if(db === null) throw new Error("DB Null");
return handler(value);
});
}
Which would let you do:
function callDBDestroy() {
var db;
var w = withDb(db); // or whatever instance
DB_Categories.destroy().then(w(function () {
// do stuff
}))); // .then(w( to chain calls here.
...
}
I want to return an anonymous promise which returns nothing as none of the promises have any data I am concerned with. Something like:
new Promise().resolve();
You are looking for Promise.resolve(undefined). Though you can omit the undefined, that's implicit.
….then(function () {
if (DB_Equipment != null) {
return DB_Equipment.destroy();
} else {
return Promise.resolve(undefined);
}
}).…
And you don't even have to return a promise from a then callback, simply returning undefined (or not returning) will have the same effect.
….then(function () {
if (DB_Equipment != null) {
return DB_Equipment.destroy();
}
}).…
In your case, I'd recommend a wrapper function:
function destroyDatabase(db, name = "db") {
if (db != null)
return db.destroy().catch(err => {
showMsg(`Error in destroying ${name}: ${err}`);
});
else
return Promise.resolve();
}
function callDBDestroy() {
return destroyDatabase(DB_Categories, "categories")
.then(() => destroyDatabase(DB_Certificates, "certificates"))
.then(() => destroyDatabase(DB_Locations, "locations"))
}
// or even in parallel:
function callDBDestroy() {
return Promise.all([
destroyDatabase(DB_Categories, "categories"),
destroyDatabase(DB_Certificates, "certificates"),
destroyDatabase(DB_Locations, "locations")
]);
}
How about using an Array, since you do the very same task, and only the DB changes:
//serial
function callDBDestroy() {
var databases = [
DB_Categories,
DB_Equipment,
DB_Certificates,
DB_Locations
];
function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };
databases.reduce(
(prev, db) => db == null?
prev:
prev.then(() => db.destroy().catch(errorMessage)),
Promise.resolve()
)
}
//parallel
function callDBDestroy() {
var databases = [
DB_Categories,
DB_Equipment,
DB_Certificates,
DB_Locations
];
function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };
databases.forEach( db => db && db.destroy().catch(errorMessage) );
}
I've added a serial and a paralell version.
It seems that you can DRY this out and replace a lot of redundant code by using an array of databases and then just loop through the array:
function callDbDestroy();
var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];
// chain all the destroys together
return dbs.reduce((p, db) => {
return p.then(() => {
if (db) {
return db.destroy().catch(err => {
showMsg("Error in callDBDestroy: " + err);
});
}
});
}, Promise.resolve());
}
You do not have to return a promise from a .then() handler. If you just have no return value, then it's just like doing return undefined which just means that no value will be passed to the next .then() handler, but the promise chain will continue just fine. Conceptually, it works the same as return Promise.resolve(), but there's no need to make an extra promise there.
Since you aren't passing a value from one .then() to the next in the chain, you have nothing to pass there so you can just not return anything if there's no db value to call destroy on.
FYI, using .reduce() to loop through an array is with the return p.then(...) structure is a common design pattern for sequencing async operations on an array.
FYI, using the Bluebird promise library (which has some useful helpers), this could be done like this:
function callDbDestroy();
var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];
return Promise.mapSeries(dbs, db => {
if (db) {
return db.destroy().catch(err => {
showMsg("Error in callDBDestroy: " + err);
});
}
});
}
For more info on why the Bluebird (or other promise libraries) are still useful even with ES6, see Are there still reasons to use promise libraries like Q or BlueBird now that we have ES6 promises?
Since it appears that these databases might all be independent, I'm wondering why you are forcing them to be executed in sequence. If they don't have to be forced into sequence, then you could do this:
function callDbDestroy();
var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];
return Promise.all(dbs.map(db => {
if (db) {
return db.destroy().catch(err => {
showMsg("Error in callDBDestroy: " + err);
});
}
}));
}
Since this runs the operations in parallel, it has the opportunity for faster end-to-end execution time vs. strict serialization.

JavaScript - Return promise AND/OR call callback?

I often see in other peoples docs something like:
Callback is optional, if omitted returns a promise.
This is what I have:
export function doSomeAsync(options, callback) {
const useCallback = (callback && typeof callback == 'function');
const promise = new Promise((resolve, reject) => {
// --- do async stuff here ---
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
finish(true, "Number is 1");
} else {
finish(false, new Error("Number is not 1"));
}
}, 1000);
// ---------------------------
function finish(ok, rtn) {
if (useCallback) {
if (ok) {
callback(null, rtn);
} else {
callback(rtn, null);
}
} else {
if (ok) {
resolve(rtn);
} else {
reject(rtn);
}
}
}
});
return (useCallback) ? false : promise;
}
The finish() function just avoids lots of if... statements scattered around.
I'm creating a promise object, whether or not I use it.
Testing like this:
doSomeAsync({ num: 1 }).then((result) => {
console.log('p result', result);
}).catch((err) => {
console.log('p err', err);
});
doSomeAsync({ num: 1 }, (err, result) => {
if (err) {
console.log('cb err', err);
} else {
console.log('cb result', result);
}
});
This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?
This could be simplified if you simply always used the promise, which you're always creating anyway:
export function doSomeAsync(options, callback) {
const promise = new Promise((resolve, reject) => {
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
resolve("Number is 1");
} else {
reject(new Error("Number is not 1"));
}
}, 1000);
});
if (callback && typeof callback == 'function') {
promise.then(callback.bind(null, null), callback);
}
return promise;
}
Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.
You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:
const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
(resolve, reject) => doStuffWith(stuff, resolve, reject)
)
.then(response => callback(null, response))
.catch(callback);

Categories