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.
Related
When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});
I have the following snippet of code inside asynchronous function -
await application.save((err) => {
console.log("hello");
if (err) {
res.status(500).send({ message: "Error encountered" });
}
});
console.log("hey");
Why does "hey" get printed out earlier than "hello"? And how to fix it so the behaviour is as expected (asynchronous save operation is waited for and only when it's done and "hello" is printed, "hey" should be printed).
Following code does actually save object to MongoDB, but when I use application.save().then(() => {}) I get an error "Cannot read property 'then' of undefined"
You are confusing between callbacks and promises.
For example, we have first task F, long task T and next task N. We want to ensure the code run in the order F -> T -> N. To do that, we need either callbacks or promises.
Callbacks
function F() {}
function T(cb) {
// do that long task here
cb(err); // then call the cb and pass is the err
}
function N() {}
function main() {
F();
cb = (err) => {
if (err) { // handle err }
else {
N() // N needs to be run in cb
}
// clean up
// N can also be put here if you want to guarantee N will run
};
T(cb);
}
Promises
function F() {}
function T(cb) {
return new Promise((resolve, reject) => {
// do that long task here
if(err) {
reject();
} else {
resolve();
}
});
}
function N() {}
// using .then .catch .final
function main() {
F();
T().then(N())
.catch(() => {
// handle error
})
.finally(() => {
// clean up
// N can also be put here if you want to guarantee N will run
})
}
// using async/await
async function main() {
F();
try {
await T();
N();
} catch {
// handle error
} finally {
// clean up
// N can also be put here if you want to guarantee N will run
}
}
In your case, save() function does not return a promise but expects a callback to be passed in. If you want a quick and simple solution, put your console.log("hey"); in the callback. A better way is to promisify that callback function so it returns a promise and you can await it. Here is a sample promisify function to turn functions accepting callbacks into functions returning promises:
From
function T(...args, cb) {
// do that long task here
cb(err, data)
}
To
function TAsync(...args) {
return new Promise((resolve, reject) => {
T(...args, function(err, data) {
if (err) reject(err)
else resolve(data)
});
});
}
if a function returns a promise you just need to use the await and assign the return to a variable which is the response. You shoud always use a try catch because promises can launch exceptions.
function divide(i, j) {
return new Promise((resolve, reject) => {
if(j == 0) {
reject("division by zero.");
} else {
resolve(i / j);
}
});
}
async function main() {
try {
let resp = await divide(5, 2);
console.log(`resp = ${resp}`);
resp = await divide(2, 0);
console.log(`resp 2 = ${resp}`);
} catch(e) {
console.log(`catch = ${e}`);
}
}
// another way to use promises
divide(5,2).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
divide(5,0).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
main();
probably your function isn't returning a promise.
I'd like some help please as I'm quite new in node.js and working with node packages.
I'm having the following script which makes a GET http request running on node using request which is deprecated now
const foo = (bar, callback) => {
const url = 'https://some.api.com?key=abc123';
request({url: url, json: true}, (error, response) => {
if (error) {
callback('Oops, there is an error!', undefined);
} else if(response.body.foobarArray.length === 0) {
callback('No data found', undefined);
} else {
callback(undefined, {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
})
}
});
}
console.log(foo('Hello')); // this logs {foobar1: 'Hello', foobar2: 'World'}
I'm trying to rewrite it using axios instead, so this is my code
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
const response = await axios.get(url);
if (response.body.foobarArray.length === 0) {
return 'No data found';
} else {
return {
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
};
}
} catch (error) {
return 'Ooops! Something went wrong :(';
}
};
console.log(foo('Hello')); // This logs `Promise { <pending> }`
I'm not sure what I'm doing wrong here as I'm not very familiar how promises work exactly, but how can I fix this?
const foo = async (bar) => {
const url = 'https://some.api.com?key=abc123';
try {
return await axios.get(url).then(response => {
return new Promise((resolve, reject) => {
if (response.body.foobarArray.length === 0) {
return reject('No data found');
} else {
return resolve({
foobar1: response.body.foobar1,
foobar2: response.body.foobar2,
});
}
})
}).catch(err => {
return Promise.reject(err);
});
} catch (error) {
// return 'Ooops! Something went wrong :(';
return Promise.reject(`an error occurred : ${error}`);
}
};
foo('hello').then(result => {
console.log(result);
}).catch(err => {
console.log(`error ! : ${err}`);
});
async functions returns a promise. async functions use an implicit Promise to return its result. Even if you don't return a promise explicitly async function makes sure that your code is passed through a promise
as you are using axios asynchronous , it's response is a promise which must be handled inside .then().catch() functions .
if no error occurs you can access the response inside your .then() , else you will have access to your error on .catch()
inside your .then() you can now do what you want with data , returning a new Promise , using resolve() for success and reject() for failure .
You have 2 options here:
Option 1
Any async function returns a Promise (behind the scenes) so:
foo('Hello').then(console.log).error(console.error);
Option 2
You need to await for the result of foo function but, at the moment, you can't use await out of function scope level. So:
async function main() {
try {
const result = await foo('Hello');
console.log(result);
} catch (err) {
console.error(err);
}
}
main();
In future Node.js releases, using await at global scope will be allowed.
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...
});
here are two method .but i need it to finish sync.But it is failed.
I want to use the Promise.all to keep it sync but i find the images process is async and it can't reject or resolve in a loop! It really makes me confused .
below is my code.I can't catch the res or err. it just end after processed the images...even don't excute the test Method,but i wonder that Promise.all is a sync method? And is there any way to catch the res or err information? Thanks !
var gm = require('gm')
var fs = require('fs')
var config = require('./imgConfig')
var image = ['1.jpg', '2.jpg', '3.jpg', '4.jpg','5.jpg','6.jpg']
var _image = ['_1.jpg', '_2.jpg', '_3.jpg', '_4.jpg','_5.jpg','6.jpg']
testS()
function testS() {
uploadFiles(image, _image, 'subModule').then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
}
function uploadFiles(inputArray, outputArray, type) {
return Promise.all([multipleResize(inputArray, outputArray, type), test()])
}
function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("object");
resolve("yeah")
}, 100)
})
}
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
imgConfig
module.exports = {
'subModule': {
width: 300,
height: 300
},
inputPath:'./input/',
outputPath:'./output/'
}
In multipleResolve, you are running return resolve() as part of the #forEach() call. This probably isn't what you want, as the first iteration will resolve the promise you returned, even though all of your gm .write() calls won't be finished yet.
I would consider doing something like this -- note the internal use of Promise.all() and the inputArray#map() to convert each img/index pair to their own Promise. I also swapped around some of the error logic to make sure the errors are checked as soon as possible.
function multipleResize(inputArray, outputArray, type) {
var height = config[type].height;
var width = config[type].width;
var flag = 0;
return Promise.all(inputArray.map((img, index) => {
return new Promise((resolve, reject) => {
gm(config.inputPath + img)
.resizeExact(width, height)
.noProfile()
.write(config.outputPath + outputArray[index], function (err) {
if (err) {
console.error(err);
return reject(err);
}
try {
fs.unlinkSync(config.inputPath + img); // consider using an async method here as well.
console.log("next" + config.outputPath + outputArray[index]);
flag++;
console.log('ko');
return resolve();
} catch (e) {
console.error(e);
reject(e);
}
});
});
}));
}
Side note, you seem to be using promises (mostly) correctly, but you are incorrectly calling them sync, when they typically aren't synchronous.