I pass a callback to fs.writeStream, and for some reason any tasks after it are being called first, before the callback is executed.
async function writeFile(file, callBack) {
// awaiting this also doesn't work
fs.writeFile(arg1, arg2, arg3, (error) => callBack(error))
}
async function task() {
let count = 0;
const callBack = (error) => {
if (error) {
count--;
} else {
count++;
}
}
for(const file of files) { // files.length = 1
await writeFile(file, callBack);
}
console.log(count) // Prints 0, should be 1. Why does this get called before the callback?
}
First of all, you are calling it wrong. If you want to wait for an async function you have to call it like this with await
await yourFunction(...);
and not like this with async
async yourFunction();
Second, you are obviously mixing async functions and functions with callbacks. Ie, when you have
async function yourFunction() {
anotherFunction((error, data) => {
...
});
}
you are actually returning a Promise<void> which immediately resolves. async function do not wait for any callbacks of callback-based functions.
And third, you are not using the file parameter of your writeFile(file, callBack) function, but three completely undefined arguments arg1, arg2, arg3
For your current problem, there are two possibilities
You import the promise functions of the fs module and use them properly (available since Node v10)
const fsp = require("fs").promises;
async function writeFile(filename, data, errorHandler) {
try{
await fsp.writeFile(filename, data);
} catch (e) {
errorHandler(e);
}
}
You import the classic callback-form of the fs module and wrap the functions into a promise
const fs = require("fs");
async function writeFile(filename, data, errorHandler) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, data, (e) => {
if (e) {
errorHandler(e);
// if you want to continue and adjust the count
// even if the writeFile failed
// you don't need the reject here
return reject(e);
}
resolve();
});
});
}
Given you imported the correct fs library, namely fs/promises, your answer should look like this:
async function writeFile(file) {
await fs.writeFile(file);
}
async function task() {
let count = 0;
const callBack = (error) => {
if (error) {
count--;
} else {
count++;
}
}
for(const file of files) { // files.length = 1
await writeFile(file);
}
console.log(count) // Prints 0, should be 1. Why does this get called before the callback?
}
You were missing the required await to actually await the asynchronous result of fsPromises.writeFile as you can see here: https://nodejs.org/dist/latest-v15.x/docs/api/fs.html#fs_fspromises_writefile_file_data_options
Also if you're using the promise based fs you will not have nor need the callback (ftfy)
If you still want to count, do it like this:
async function writeFile(file) {
await fs.writeFile(file);
}
async function task() {
let count = 0;
for (const file of files) { // files.length = 1
try {
await writeFile(file);
count++; // <-------- NOTICE THIS PART HERE
} catch (e) {
count--;
}
}
console.log(count) // Prints 0, should be 1. Why does this get called before the callback?
}
Related
In a VSCode extension I have a function that removes certain directories from the current workspace. But the function does not wait for the directories to be removed.
The accepted answer for this Stackoverflow question makes sense, but still I can't figure out how to make my function work correctly. Where do I put await and return?
async function cleanUpWorkspace(workspace: any) {
const fs = require('fs');
const rimraf = require("rimraf");
var targetFolders = ['dirA', 'dirB', 'dirC', 'dirD'];
try {
await targetFolders.forEach(function (value) { //'await' has no effect on the type of this expression.
let targetPath = workspace + "\\\\" + value;
if (fs.existsSync(targetPath)) {
fs.promises.access(targetPath);
rimraf(targetPath, function (err: any) {
if (err) {
console.log(err);
} else {
console.log("Directory: " + value + " was deleted");
}
});
} else {
console.log(value + " doesn't exist.");
}
});
return Promise.resolve();
} catch (error) {
console.log(error);
}
}
async function prepare() {
await cleanUpWorkspace(workspace);
}
Typescript 4.3.5
node.js 14.17.0
You can only use await inside an async function or an async arrow function (async () => { /* function contents */ })
You should only use await on functions that return a Promise
All async functions, regardless of whether or not they return a value, return a Promise. If it doesn't return a value inside, the return type is Promise<void> (similar to how regular functions implicitly return void if they don't have a return value)
You are trying to do async operations (i.e call functions that return promises) inside the function passed to forEach. Ideally, we would await them. To use await, we need to make the function passed to forEach an async function. However, forEach does not internally await promises. This means that uncaught exceptions inside your forEach function do not make it up to the try/catch block.
To avoid this, we can use a for..of loop.
Because async functions automatically return a Promise, there's not really a reason you need to return Promise.resolve()
try {
for (const value of targetFolders) {
let targetPath = workspace + "\\\\" + value;
if (fs.existsSync(targetPath)) {
await fs.promises.access(targetPath);
rimraf(targetPath, function (err: any) {
if (err) {
console.log(err);
} else {
console.log("Directory: " + value + " was deleted");
}
});
} else {
console.log(value + " doesn't exist.");
}
}
} catch (error) {
console.log(error);
}
Now, we can await the fs.promises.access call.
Another thing we can do is promisify rimraf. There is a function in the Node standard library -- util.promisify -- that converts functions taking callbacks into functions returning promises. So, if we have
const util = require("util")
const rimrafPromise = util.promisify(rimraf)
then we can do
try {
for (const value of targetFolders) {
let targetPath = workspace + "\\\\" + value;
if (fs.existsSync(targetPath)) {
await fs.promises.access(targetPath);
await rimrafPromise(targetPath);
console.log("Directory: " + value + " was deleted");
} else {
console.log(value + " doesn't exist.");
}
}
} catch (error) {
console.log(error);
}
I want to use the async function to bring out a particular value from my database to my the function global so I can use it in other parts of my application.
async function dimension() {
const result = await Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
console.log(holder) /// this print the expected result, that i want to make global
});
return {
result
};
};
console.log(dimension())
but the console.log of the dimension() gives me this
Promise { <pending> }
instead of the same value that
console.log(holder)
gives me nothing.
The problem is you are printing the result of dimension() as soon as you call it, but since this function is async, it returns a promise that is not yet resolved.
You do not need to use async/await here. Settings.find() seems to return a Promise. You can just return directly this Promise and use .then() to do something once that promise is resolved.
Like this :
function dimension () {
return Settings.find({ _id: '5d7f77d620cf10054ded50bb' }, { dimension: 1 }, (err, res) => {
if (err) {
throw new Error(err.message, null);
}
return res[0].dimension;
});
}
dimension().then(result => {
//print the result of dimension()
console.log(result);
//if result is a number and you want to add it to other numbers
var newResult = result + 25 + 45
// the variable "newResult" is now equal to your result + 45 + 25
});
More info on Promises and async/await
You have to await for your result, like this:
const result = await dimension();
console.log(result);
In that case, you don't even to make the original function async, just write it like this:
function dimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
};
async function myGlobalFunc() {
const result = await dimension();
console.log(result);
}
The best way to have this globally available is to just put your function dimension in a file somewhere. Then where you need the value, you just require it and await its value. E.g.
// get-dimension.js
// ...const Settings = require... comes here
module.exports = function getDimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
}
// your other modules, e.g.
// my-service-handler.js
const getDimesion = require('./get-dimension');
async function myServiceHandler() {
const dimension = await getDimension();
// do stuff with dimension.
}
You're using async/await, but you're mixing it with callbacks, this is not desirable as it leads to confusion. It's not clear what you expect to happen in the callback, but return holder; likely doesn't do what you expect it to do, returning from a callback does not work the same way returning from a promise handler works. Your entire implementation should work with promises so that the async/await syntax reads more naturally (as it was intended).
async function dimension() {
// We're already awaiting the result, no need for a callback...
// If an error is thrown from Settings.find it is propagated to the caller,
// no need to catch and rethrow the error...
const res = await Settings.find({_id: "5d7f77d620cf10054ded50bb"}, {dimension: 1});
return {result: res[0].dimension};
}
(async () => {
try {
console.log(await dimension());
} catch (err) {
console.error(err);
}
})();
Use dimension().then() in your code then it will work fine.
async function globalDimension() {
const data = await Users.findOne({ phone: 8109522305 }).exec();
return data.name;
}
globalDimension().then(data => {
console.log(data);
});
How can I know when all promises inside the for loop finished executing? Is there a better way to do it?
for (let index = 0; index < array.length; index++) {
request(array[index], function (error, response, body) {
promise1().then(result1 => {
if (result1 !== 0) {
promise2().then(result2 => {
promise3(result2);
}
} else {
promise3(result1);
}
});
});
}
console.log('all promises finished');
This is made easier by transforming the code to use async/await to map the items into promises, then using the standard Promise.all() function to wait for all of the promises to resolve:
// promisified request()
const requestP = item =>
new Promise((resolve, reject) => {
request(item, (error, response, body) => {
if (error) return reject(error);
resolve({ response, body });
});
});
const processItem = async item => {
const { response, body } = await requestP(item);
const result1 = await promise1(/* this probably uses response or body? */);
if (result1 !== 0) {
const result2 = await promise2();
return await promise3(result2);
} else {
return await promise3(result1);
}
};
const promises = array.map(processItem);
Promise.all(promises).then(() => {
console.log("all promises finished");
});
You can keep adding the promises you create in a list inside the for loop.
Once you are done with your loop, you can register a Promise.all method.
let list_of_promises = [];
for(...) {
list_of_promises.push(
new Promise(function(resolve, reject)) {
...
}
)
}
Promise.all(list_of_promises).then(function(data)) {
...
}.catch(function(err) {
...
});
If all your promises get resolved, .then method will be called.
Note: Even if one of your promise fails, it will go inside the .catch method.
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));
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