I have this code:
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0;
} catch (err) {
return err;
}
}
getURL().then( result => {
if (result === 0) console.log("success");
else console.log(result);
});
The fetch will fail and the error is logged to the console. How do I rework the code so it uses async and try/catch everywhere? That is, I'm looking to avoid doing getURL().then for the sake of consistency.
EDIT:
For those downvoting me, await getURL() won't work as it's invalid syntax.
EDIT2:
Tried this but it didn't catch the error:
async function getURL() {
return await fetch("http://www.blah.com");
}
let result = async function() {return await getURL();}
try {
result();
} catch (e) {
console.log(e);
}
You can wrap your whole code inside an instantly executed async function like this:
// service.js
async function getURL() {
return await fetch("http://www.blah.com");
}
// your.module.js
(async function() {
// do things...
try {
let result = await getURL();
} catch (e) {
console.log(e);
}
// do things...
res.send({});
});
Every time you need to catch an error from promise, either using new Promise, async-await or generator you need to use .then() or you can do something like this another async-await.
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0; // EDIT: just returning value which is success
} catch (err) {
return err; // EDIT: returning value not rejecting a promise
}
}
async function main () {
try {
let result = await getURL();
if (result === 0) console.log("success");
console.log(result); // EDIT: error will be print.
}
catch (err) { // EDIT: getURL() never rejects so always success.
console.log(err);
}
});
main();
This situation doesn't really occurs as while our main function in server-side or client-side are async and handling this for us.
Like using express:
app.post('/api', async (req, res) => {
try {
let result = await getURL();
res.send(async);
}
catch(err) {
res.send(err);
}
});
EDIT: asyn-await doesn't reject or resolve a call, just return a value. thus must be used carefully.
function fetch(url) {
return new Promise( (resolve, reject) => {
let x = Math.floor(Math.random() * Math.floor(9) + 1);
// 50-50 resolve or reject
if(x%2===0) return resolve(false); //resolve with `false` statement
reject(true); // reject with `true` still a reject
});
}
async function getURL() {
try {
await fetch("http://www.blah.com");
return 0; // if fetch resolve
} catch (err) { //only if fetch reject
return err;
}
}
async function main () {
try {
let result = getURL();
if (result === 0) console.log("success"); //getURL never reject any call
console.log(result);
}
catch (err) { // getURL doesnt reject
console.log(err);
}
};
main();
I realize now async functions always return a promise. Even if you throw an error it still gets wrapped up into a promise. Therefore using try/catch won't help. This is how I ended up writing the code:
async function getURL() {
return await fetch("http://fake");
}
getURL().then( () => console.log("success")).catch( (e) => console.log(e));
Related
Below is my function to return SQL query result.
function getPriorityList(operatorId) {
try {
const result = Api.getApisDetail(operatorId);
return result;
} catch (error) {
console.log(error);
}
}
you should wait for promise fulfillment
try this :
async function getPriorityList(operatorId) {
try {
const result = await Api.getApisDetail(operatorId);
return result;
} catch (error) {
console.log(error);
}
}
It appears that the promise implementation is incorrect in this case. Try something along these lines:
function getPriorityList(operatorId) {
Api.getApisDetail(operatorId).then((result) =>{
console.log(result);
return result;
}).catch((error)=>{
return error;
});
}
The "Api.getApisDetail" function must also return promise in this case.
I'm reading data from db with using await so I used Promise but the function seems to return nothing
async function read() {
return new Promise((resolve, reject) => {
const db = new DB();
db
.read()
.then(result => {
resolve(result);
}).catch(() => {
reject('db-error');
});
});
}
(async () => {
const data = await read();
console.log(data); // undefined
})();
How can I make read() return result?
You are making it more complicated than it has to be. If you are already using an API that returns a promise then there is no need to use the promise constructor yourself.
And declaring a function as async is only necessary if you are using await in it to deal with promises.
So either do:
function read() {
const db = new DB();
return db
.read()
.catch(() => {
return 'db-error';
});
}
Or
async function read() {
const db = new DB();
try {
return await db.read();
} catch(error) {
return 'db-error';
}
}
If you are still not getting the value you want then you are not using the database API correctly and you have to read its documentation to figure out how to get back the right data.
The awesome guys who write the MDN Web Docs say that the result of await will be undefined if the promise that is being waited on is rejected: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#handling_rejected_promises
Check out the following scenario.
This is a simple function that returns a Promise:
function asyncFunc(waitTime) {
return new Promise((resolve, reject) => {
setTimeout(() => {
// say we prefer people who do things in 3 seconds or less
if (waitTime <= 3000) {
resolve('Promise resolved! You\'re fast! :)');
} else {
reject('Promise rejected! You\'re slow! :(');
}
}, waitTime);
});
}
Let's test the function using a method similar to yours:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime);
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3000); // Returns `Promise resolved! You're fast! :)`, as expected
testAsyncFunc(3001); // Returns `undefined` instead of `Promise rejected! You're slow! :(`
But since we want the actual rejection error of the asynchronous operation instead of undefined, the solution is to chain catch to the await statement to catch any rejection errors immediately you call the asynchronous function and then throw the error so it can be caught by any catch error handler you may want to use, like so:
async function testAsyncFunc(waitTime) {
try {
const result = await asyncFunc(waitTime)
.catch(error => {
// throw the rejection error so it can be handled by the catch block below
throw new Error(error);
});
// if no errors
console.log(result);
} catch(error) {
console.error(error.message);
}
}
testAsyncFunc(3001); // Returns the expected result: `Promise rejected! You're slow! :(`
I am new in Node.js, I have async method called
async function makeLineStringFromPoints(user) {
...
for (let item of linesGeoJson) {
saveLineIntoDb({
'line': item,
'date': user.date_created,
'user_id': user.uid,
'device_id': user.devid,
}).then((userStored) => {
console.log(userStored); // i received undefined
}).catch((err) => {
console.log(err);
});
}
...
}
in this method I have invoke other async method saveLineIntoDb in this method I an storing user information in database :
async function saveLineIntoDb(user) {
let userStored = user;
try {
await db.result(pgp.helpers.insert(user, cs))
.then(data => {
return await (user); // return user
});
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
Now after storing I want to return user in saveLineIntoDb(user) to }).then((userStored) but always I got undefined.
How can I do that ?
saveLineIntoDb doesn't return the result and mixes async and raw promises. It also prevents errors from being handled in caller function. return await (user) is excessive, it also won't work because it happens inside regular function.
It should be:
async function saveLineIntoDb(user) {
await db.result(pgp.helpers.insert(user, cs));
return user;
}
While caller function doesn't chain promises and doesn't make use of await. If DB queries should be performed in series, it should be:
async function makeLineStringFromPoints(user) {
...
try {
for (let item of linesGeoJson) {
const userStored = await saveLineIntoDb(...);
console.log(userStored);
}
} catch (err) {
console.log(err);
}
}
If DB queries should be performed in parallel, it should be:
async function makeLineStringFromPoints(user) {
...
try {
await Promise.all(linesGeoJson.map(async (item) => {
const userStored = await saveLineIntoDb(...));
console.log(userStored);
}));
} catch (err) {
console.log(err);
}
}
Why you are returning user inside the then? May be can do in this way
async function saveLineIntoDb(user) {
let userStored = user;
try {
await db.result(pgp.helpers.insert(user, cs));
return user; // return user
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
In saveLineIntoDb method try return result of await:
async function saveLineIntoDb(user) {
let userStored = user;
try {
return await db.result(pgp.helpers.insert(user, cs))
.then(data => {
return await (user); // return user
});
} catch (error) {
logger.error('saveIntoDatabase Error:', error);
}
}
(This function, you don't need use async/await)
I'm fairly new to async await in javascript so this question might be something I don't know.
I have this
async function foo(req, res, next) {
try {
await scan(req.params.stack);
res.send('ok');
} catch (err) {
res.status(500).send('fail');
}
}
async function scan(stack) {
try {
const libs = [1,2,3];
const promises = libs.map(async l => analyze(stack, l)
.catch((err) => { throw new Error(err); }));
return q.allSettled(promises)
.then((results) => {
const rejected = results.filter(r => r.state === 'rejected');
if (rejected.length === results.length) throw new Error('Failed');
return results;
})
.catch((err) => {
throw new Error(err);
});
} catch (err) {
throw new Error(err);
}
}
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
} catch (err) {
return Promise.reject('Error while trying to analyze libs');
}
}
Somehow I'm getting this wild warning and I don't know where I am not catching the error.
Of course, I'm making build configuration fail in order to test the error, but instead of having a normal flow cathing the error I got this:
(node:415) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): Error: Error while trying to analyze libs
Am I using async await good? Is there any pattern I should follow in order to chain async await?
The wild thing is that the foo function works well, meaning that the res.status.(500).send('fail'); works and I'm getting the response
When I was using native promises this error didn't appear.
I'm really stuck here
While using async-await scan function you were mixing .then() .catch() waterfall with await. async-await handles promises as good as .then(). So stick with one flow and try to mix both in one function or one inside another.
async foo(req, res, next) {
try {
await scan(req.params.stack);
res.send('ok');
} catch (err) {
res.status(500).send('fail');
}
}
async scan(stack) {
try {
const libs = [1,2,3];
// This libs.map functions return promise. then why not use await?
const promises = await libs.map(async l => analyze(stack, l);
// Again q.allSettled returns promise, use await here too
let results = await q.allSettled(promises);
const rejected = results.filter(r => r.state === 'rejected');
if (rejected.length === results.length) throw new Error('Failed');
return results;
}
// If any promise call reject function will be in catch
catch (err) {
throw new Error(err);
}
}
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
}
catch (err) {
console.log(err);
return null;
}
}
Calling an async function (here, analyze) would return a promise, which will resolve or reject according to the return value of the async function or whether an error was thrown.
Now, the analyze function is handling the error thrown but it will return a Promise.reject() when an error is thrown. A Promise.reject() is the unhandled rejection here, which is what the log is stating.
In terms of a synchronous function the equivalent will be
function sync() {
try {
// do something dangerous
} catch (ex) {
throw Error('Something bad happened'); // this error is still being thrown and nobody is handling it
}
}
To handle this error you can do the following when you are calling sync, wrap it in try and catch again
try {
sync();
} catch (ex) {
console.error(ex); // not gonna throw another exception, otherwise the program might crash
}
Now, the equivalent of this wrap for the analyze function will be using another async function, or better since calling async function will return a Promise, use the catch method of a Promise
analyze()
.then(() => console.log('My work is done here'))
.catch(ex => console.error(ex)); // NOTE: not throwing another exception
Even better would be to not return a rejection from catch in the first place, thus making analyze,
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
} catch (err) {
console.error(err); // not eating up good errors with something vague is always good
return null; // or something else to signify that insert failed
}
}
In the analyze() you are returning Project.reject() but analyze() is an async function. Therefor it resolves any value that you return and rejects any error you throw.
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
} catch (err) {
return Promise.reject('Error while trying to analyze libs');
}
}
So when the analyze function catches an error you are creating a rejection but then resolving the function. So Promise.reject('Error while trying to analyze libs'); is not being handled. Since async functions always return a promise that resolves with whatever you return and rejects whatever you throw, your analyze function is always going to resolve. Try doin this...
async function analyze(stack, libraries) {
try {
const config = await buildConfiguration(stack, libraries);
return await databaseInsertion(vulnsObject);
} catch (err) {
throw Error('Error while trying to analyze libs');
}
}
The other thing I see as a possible problem in this code is even though you pass the map(async func) an async function, it doesn't care. It won't wait for each function to complete before calling the next.
const promises = libs.map(async l => analyze(stack, l)
.catch((err) => { throw new Error(err); }));
return q.allSettled(promises)
.then((results) => {
const rejected = results.filter(r => r.state === 'rejected');
if (rejected.length === results.length) throw new Error('Failed');
return results;
})
.catch((err) => {
throw new Error(err);
});
There are two changes bellow
const promises = libs.map(async l => await analyze(stack, l)
.catch((err) => { throw new Error(err); }));
return q.allSettled( await promises)
.then((results) => {
const rejected = results.filter(r => r.state === 'rejected');
if (rejected.length === results.length) throw new Error('Failed');
return results;
})
.catch((err) => {
throw new Error(err);
});
I added an await before the analyze function and an await before passing the promises variable into q.allSettled().
To overcome callback hell in javascript, I'm trying to use async await from legacy code written in SQLServer procedure.
But I'm not sure my code might be write properly.
My first confusing point is when async function returns, should it return resolve() as boolean, or just return reject and handle with try-catch?
Here is my code snippets.
Please correct me to right direction.
apiRoutes.js
app.route('/api/dansok/cancelDansok')
.post(dansokCancelHandler.cancelDansok);
dansokCancelController.js
const sequelize = models.Sequelize;
const jwt = require('jsonwebtoken');
async function jwtAccessAuthCheck(accessToken) {
if (!accessToken) {
return Promise.reject('Empty access token');
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
return Promise.reject('TokenExpiredError.');
} else {
return Promise.resolve();
}
});
}
async function checkFeeHist(dansokSeqNo) {
let feeHist = await models.FeeHist.findOne({
where: { DansokSeqNo: dansokSeqNo}
});
return !!feeHist;
}
async function getNextDansokHistSerialNo(dansokSeqNo) {
....
}
async function getDansokFee(dansokSeqNo) {
....
}
async function doCancel(dansokSeqNo) {
try {
if (await !checkFeeHist(dansokSeqNo)) {
log.error("doCancel() invalid dansokSeqNo for cancel, ", dansokSeqNo);
return;
}
let nextDansokSerialNo = await getNextDansokHistSerialNo(dansokSeqNo);
await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo);
await updateDansokHist(dansokSeqNo);
await updateVBankList(dansokSeqNo, danokFee.VBankSeqNo);
await getVBankList(dansokSeqNo);
} catch (e) {
log.error("doCancel() exception:", e);
}
}
exports.cancelDansok = function (req, res) {
res.setHeader("Content-Type", "application/json; charset=utf-8");
const dansokSeqNo = req.body.DANSOKSEQNO;
const discKindCode = req.body.HISTKIND;
const worker = req.body.PROCWORKER;
const workerIp = req.body.CREATEIP;
const accessToken = req.headers.accesstoken;
//check input parameter
if (!dansokSeqNo || !discKindCode || !worker || !workerIp) {
let e = {status:400, message:'params are empty.'};
return res.status(e.status).json(e);
}
try {
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
doCancel(dansokSeqNo).then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
});
});
} catch(e) {
return res.status(e.status).json(e);
}
};
You'll need to rewrite jwtAccessAuthCheck(accessToken) so that it keeps track of the outcome of its nested tasks. In the code you've written:
// Code that needs fixes!
async function jwtAccessAuthCheck(accessToken) {
// This part is fine. We are in the main async flow.
if (!accessToken) {
return Promise.reject('Empty access token');
}
// This needs to be rewritten, as the async function itself doesn't know anything about
// the outcome of `jwt.verify`...
jwt.verify(accessToken,"dipa",function(err){
if(err) {
// This is wrapped in a `function(err)` callback, so the return value is irrelevant
// to the async function itself
return Promise.reject('TokenExpiredError.');
} else {
// Same problem here.
return Promise.resolve();
}
});
// Since the main async scope didn't handle anything related to `jwt.verify`, the content
// below will print even before `jwt.verify()` completes! And the async call will be
// considered complete right away.
console.log('Completed before jwt.verify() outcome');
}
A better rewrite would be:
// Fixed code. The outcome of `jwt.verify` is explicitly delegated back to a new Promise's
// `resolve` and `reject` handlers, Promise which we await for.
async function jwtAccessAuthCheck(accessToken) {
await new Promise((resolve, reject) => {
if (!accessToken) {
reject('Empty access token');
return;
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
reject('TokenExpiredError.');
} else {
resolve();
}
});
});
// We won't consider this async call done until the Promise above completes.
console.log('Completed');
}
An alternate signature that would also work in this specific use case:
// Also works this way without the `async` type:
function jwtAccessAuthCheck(accessToken) {
return new Promise((resolve, reject) => {
...
});
}
Regarding your cancelDansok(req, res) middleware, since jwtAccessAuthCheck is guaranteed to return a Promise (you made it an async function), you'll also need to handle its returned Promise directly. No try / catch can handle the outcome of this asynchronous task.
exports.cancelDansok = function (req, res) {
...
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
return doCancel(dansokSeqNo);
})
.then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
})
.catch(e => {
res.status(e.status).json(e);
});
};
I strongly suggest reading a few Promise-related articles to get the hang of it. They're very handy and powerful, but also bring a little pain when mixed with other JS patterns (async callbacks, try / catch...).
https://www.promisejs.org/
Node.js util.promisify