ES6 Promises confusion - javascript

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.

Related

How to make a javascript function wait for a javascript function to execute? [duplicate]

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());});

Changing script from request to axios - log pending 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.

Trouble with await/async

var images = ['image', 'image2', 'image3'];
async function proccess_images(images) {
for (const image of images) {
await postprocess(image)
}
console.log('done'); //this prints out before for loop is finished
}
async function postprocess(image) {
await process(function(data) {
console.log(data);
}, image)
};
function process(callback, image) {
3rdpartylibrary.process(image, (err, data) => {
if(err) {
console.error(err);
} else {
console.log('not done')
callback(data);
}
})
}
proccess_images(images)
I'm having trouble getting an async/await function working with my code that does some image processing using a 3rd party library. I'm trying to get each image to finish processing before moving onto the next image, but currently all three start processing immediately without waiting. It seems like the 3rd party library is starting its process which makes the async/await function think its done, but how do I make sure the next image isn't processed until AFTER 3rdpartylibrary.process has finished processing the data? I've also tried putting
await new Promise ((resolve, reject) =>
In front of the 3rdpartylibrary function which stops them all from processing at the same time, but it causes the for loop to exit after processing the first image correctly. Any ideas as to what I'm doing wrong?
Your problem appears to be that your first function isn't returning a promise. await requires a promise to work correctly. Something like this should work:
async function postprocess(image) {
var output = await process(image);
console.log(output);
};
function process(image) {
return new Promise((resolve, reject) => {
3rdpartylibrary.process(image, (err, data) => {
if(err) {
console.error(err);
reject(err);
} else {
console.log('not done')
resolve(data);
}
})
});
}
Change the process function definition to wrap a Promise:
function process(image) {
return new Promise((resolve,reject) => {
3rdpartylibrary.process(image, (err, data) => {
if (err) reject(err);
resolve(data);
})
})
}
The key point being to remove any callback references, since now it returns a Promise so you can await it.
You want to wrap the 3rdpartylibrary in a promise. What you want to do is convert the callback style to a promise by wrapping it. This is a good article to refer to. Try this:
var images = ['image1', 'image2', 'image3'];
// mock thirdPartyLibrary
const thirdPartyLibrary = {
process: (image, callback) => {
console.log(`processing image ${image}`);
setTimeout(() => {
const noError = undefined;
callback(noError, `finished procesing image ${image}`);
}, 1000);
}
}
async function proccess_images(images) {
console.log('start');
for (const image of images) {
console.log(`before processing image ${image}`);
await process(image);
console.log(`after processing image ${image}`);
}
console.log('done');
}
function process(image) {
// wrap thirdPartyLibrary in promise
return new Promise((resolve, reject) => {
thirdPartyLibrary.process(image, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
}
proccess_images(images)

Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning

I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.

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.

Categories