I'm trying to execute functions one at a time, sequentially. Using promises, I believe it should work, but for me, it does not work. I've researched somewhat and found this question, and one of the answers explains to use Promises, that is what I've been trying to do.
Here's the functions:
async function loadCommands () {
return new Promise((resolve, reject) => {
let commands = 0;
readdir('./commands/', (error, files) => {
if (error) reject(error);
for (const file of files) {
if (!file.endsWith('.js')) return;
commands++;
}
}
resolve(commands); // this is in my code, I forgot to put it - sorry commenters
});
};
async function loadEvents () {
return new Promise(async (resolve, reject) => {
let events = 0;
readdir('./events/', (error, files) => {
if (error) reject (error);
for (const file of files) {
if (!file.endsWith('.js')) return;
events++
}
});
resolve(events);
});
};
I am then using await in an async function to try and make sure it each function resolves before going onto the next function:
console.log('started');
const events = await loadEvents();
console.log(events);
console.log('load commands');
const commands = await loadCommands();
console.log(commands);
console.log('end')
In the console, this is linked (keep in mind, I have no files in ./events/ and I have one file in ./commands/):
start
0 // expected
load commands
0 // not expected, it's supposed to be 1
end
What am I doing wrong? I want these functions to be run sequentially. I've tried making it so instead of functions, it's just the bare code in the one async function, but still came to the issue.
You never resolve() the promise that you create in loadCommands, and you resolve() the promise that you create in loadEvents before the readdir callback happened.
Also, don't do any logic in non-promise callbacks. Use the new Promise constructor only to promisify, and call only resolve/reject in the async callback:
function readdirPromise(path) {
return new Promise((resolve, reject) => {
readdir(path, (err, files) => {
if (err) reject(err);
else resolve(files);
});
});
});
or simply
import { promisify } from 'util';
const readdirPromise = promisify(readdir);
Then you can use that promise in your actual logic function:
async function countJsFiles(path) {
const files = await readdirPromise(path);
let count = 0;
for (const file of files) {
if (file.endsWith('.js'))
count++;
// I don't think you really wanted to `return` otherwise
}
return count;
}
function loadCommands() {
return countJsFiles('./commands/');
}
function loadEvents() {
return countJsFiles('./events/');
}
You're trying to use await outside async. You can await a promise only inside an async function. The functions returning promises ( here loadCommands & loadEvents ) don't need to be async. Make an async wrapper function like run and call the await statements inside it like this.
PS: Plus you also need to resolve loadCommands with commands in the callback itself. Same for loadEvents. Also, remove the return and simple increment the variable when true.
function loadCommands() {
return new Promise((resolve, reject) => {
let commands = 0;
readdir('./commands/', (error, files) => {
if (error) reject(error);
for (const file of files) {
if (file.endsWith('.js')) commands++;
}
}
resolve(commands);
});
};
function loadEvents() {
return new Promise((resolve, reject) => {
let events = 0;
readdir('./events/', (error, files) => {
if (error) reject(error);
for (const file of files) {
if (file.endsWith('.js')) events++
}
resolve(events);
});
});
};
async function run() {
console.log('started');
const events = await loadEvents();
console.log(events);
console.log('load commands');
const commands = await loadCommands();
console.log(commands);
console.log('end')
}
run();
Hope this helps !
Related
I'm using the async.eachLimit function to control the maximum number of operations at a time.
const { eachLimit } = require("async");
function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
As you can see, I can't declare the myFunction function as async because I don't have access to the value inside the second callback of the eachLimit function.
You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.
Your code is a good example of the main risk: not propagating all errors safely. Read why there.
In addition, the use of async/await can make the same traps even more surprising. Compare:
let p = new Promise(resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.
with a naive (wrong) async equivalent:
let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!
Look in your browser's web console for the last one.
The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).
The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.
Since the return value of a promise constructor executor function is unused, that's bad news!
Your code
There's no reason you can't define myFunction as async:
async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
Though why use outdated concurrency control libraries when you have await?
I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:
const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)
let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;
if (op2 == null) {
throw new Error('Validation error');
}
const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
The function passed to Promise constructor is not async, so linters don't show errors.
All of the async functions can be called in sequential order using await.
Custom errors can be added to validate the results of async operations
The error is caught nicely eventually.
A drawback though is that you have to remember putting try/catch and attaching it to reject.
BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN
Throws within an async promise callback can easily be caught.
(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
or even more simply,
(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
I didn't realized it directly by reading the other answers, but what is important is to evaluate your async function to turn it into a Promise.
So if you define your async function using something like:
let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};
your turn it into a promise using:
let myPromise = f()
You can then manipulate is as a Promise, using for instance Promise.all([myPromise])...
Of course, you can turn it into a one liner using:
(async () => { code with await })()
static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res = axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}
remove await and async will solve this issue. because you have applied Promise object, that's enough.
I'm using the async.eachLimit function to control the maximum number of operations at a time.
const { eachLimit } = require("async");
function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
As you can see, I can't declare the myFunction function as async because I don't have access to the value inside the second callback of the eachLimit function.
You're effectively using promises inside the promise constructor executor function, so this the Promise constructor anti-pattern.
Your code is a good example of the main risk: not propagating all errors safely. Read why there.
In addition, the use of async/await can make the same traps even more surprising. Compare:
let p = new Promise(resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.
with a naive (wrong) async equivalent:
let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!
Look in your browser's web console for the last one.
The first one works because any immediate exception in a Promise constructor executor function conveniently rejects the newly constructed promise (but inside any .then you're on your own).
The second one doesn't work because any immediate exception in an async function rejects the implicit promise returned by the async function itself.
Since the return value of a promise constructor executor function is unused, that's bad news!
Your code
There's no reason you can't define myFunction as async:
async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}
Though why use outdated concurrency control libraries when you have await?
I agree with the answers given above and still, sometimes it's neater to have async inside your promise, especially if you want to chain several operations returning promises and avoid the then().then() hell. I would consider using something like this in that situation:
const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)
let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;
if (op2 == null) {
throw new Error('Validation error');
}
const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});
(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
The function passed to Promise constructor is not async, so linters don't show errors.
All of the async functions can be called in sequential order using await.
Custom errors can be added to validate the results of async operations
The error is caught nicely eventually.
A drawback though is that you have to remember putting try/catch and attaching it to reject.
BELIEVING IN ANTI-PATTERNS IS AN ANTI-PATTERN
Throws within an async promise callback can easily be caught.
(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
or even more simply,
(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});
I didn't realized it directly by reading the other answers, but what is important is to evaluate your async function to turn it into a Promise.
So if you define your async function using something like:
let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};
your turn it into a promise using:
let myPromise = f()
You can then manipulate is as a Promise, using for instance Promise.all([myPromise])...
Of course, you can turn it into a one liner using:
(async () => { code with await })()
static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res = axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}
remove await and async will solve this issue. because you have applied Promise object, that's enough.
I have a small problem, how to create a promise chain in a sensible way so that the makeZip function will first add all the necessary files, then create the zip, and finally delete the previously added files? (The makeZip function also has to return a promise). In the example below I don't call deleteFile anywhere because I don't know exactly where to call it. when I tried to call it inside the add file function to delete the file immediately after adding it, for some unknown reason the console displayed the zip maked! log first and then file deleted.
const deleteFile = (file, result) => {
new Promise((resolve, reject) => {
fs.unlink(`./screenshots/${file}`, (err) => {
if (err) return reject(err);
console.log(`${file} deleted!`);
return resolve();
});
});
};
const addFile = (file) => {
new Promise((resolve, reject) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
return resolve();
} catch {
return reject(new Error("failed to add file"));
}
});
};
const makeZip = () => {
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return new Promise((resolve, reject) => {
try {
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
resolve();
} catch {
return reject(new Error("failed making zip"));
}
});
})
.catch((err) => console.log(err));
};
the main cause of this is that you are not returning the promises you are instantiating in your function calls. Also I have some cool suggestion to make that can improve you code cleanliness.
[TIP]: Ever checked the promisify function in NodeJS util package, it comes with node and it is very convenient for converting functions that require callbacks as arguments into promise returning functions., I will demonstrate below anyhow.
// so I will work with one function because the problem resonates with the rest, so
// let us look at the add file function.
// so let us get the promisify function first
const promisify = require('util').promisify;
const addFile = (file) => {
// if addLocalFile is async then you can just return it
return zip.addLocalFile(`./screenshots/${file}`);
};
// okay so here is the promisify example, realized it wasn't applicable int the function
// above
const deleteFile = (file, result) => {
// so we will return here a. So because the function fs.unlink, takes a second arg that
// is a callback we can use promisify to convert the function into a promise
// returning function.
return promisify(fs.unlink)(`./screenshots/${file}`);
// so from there you can do your error handling.
};
So now let us put it all together in your last function, that is, makeZip
const makeZip = () => {
// good call on this, very interesting.
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return zip.writeZip(`./zip_files/supername.zip`);
})
.then(() => {
//... in here you can then unlink your files.
});
.catch((err) => console.log(err));
};
Everything should be good with these suggestions, hope it works out...
Thank you all for the hints, the solution turned out to be much simpler, just use the fs.unlinkSync method instead of the asynchronous fs.unlink.
const deleteFile = (file) => {
try {
fs.unlinkSync(`./screenshots/${file}`);
console.log(`${file} removed`);
} catch (err) {
console.error(err);
}
};
const addFile = (file) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
deleteFile(file);
} catch (err) {
console.error(err);
}
};
const makeZip = () => {
fs.readdirSync("./screenshots").map((file) => addFile(file));
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
};
In node.js i have a databaseMapper.js file, that uses the Ojai node MapR api. to extract data. So far i have it working with single documents, but since this is an async api, i have a bit of issues with querying multiple documents.
This is what i have so far:
function queryResultPromise(queryResult) {
//this should handle multiple promises
return new Promise((resolve, reject) => {
queryResult.on("data", resolve);
// ...presumably something here to hook an error event and call `reject`...
});
}
const getAllWithCondition = async (connectionString, tablename, condition) =>{
const connection = await ConnectionManager.getConnection(connectionString);
try {
const newStore = await connection.getStore(tablename);
const queryResult = await newStore.find(condition);
return await queryResultPromise(queryResult);
} finally {
connection.close();
}
}
here it will only return the first because queryResultPromise will resolve on the first document.. however the callback with "data" may occur multiple times, before the queryResult will end like this queryResult.on('end', () => connection.close())
i tried using something like Promise.all() to resolve all of them, but I'm not sure how i include the queryResult.on callback into this logic
This will work
const queryResultPromise = (queryResult) => {
return new Promise((resolve, reject) => {
let result = [];
queryResult.on('data', (data) => {
result.push(data)
});
queryResult.on('end', (data) => {
resolve(result);
});
queryResult.on('error', (err) => {
reject(err);
})
});
};
Im trying to use async await on a function that returns a promise but the out put im getting is Promise { <pending> }. In here im using function called convertFiletoPDF which returns a promise. I need to get the output (the path that i have mention in resolve() ).
When i use it as
convertFiletoPDF(file).then((result) => {
console.log(result);
}).catch((err)=>{
console.log(err);
});
it gives the expected result.Whats wrong with the code below? im quite new to these async await and promises.
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
unoconv.convert(file, "pdf", function(
err,
result
) {
if (err) {
reject(err);
}
let File = file.substring(file.lastIndexOf("/")+1,file.lastIndexOf("."));
// result is returned as a Buffer
fs.writeFile(__dirname+"/files/converted/"+File+".pdf", result, error => {
/* handle error */
if (err) reject(error);
else resolve("./files/converted/"+File+".pdf");
});
});
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc(file);
console.log(res);
The return value of an async function is a promise, so naturally that's what your console.log outputs. You need to either consume the result via await (within another async function) or use then/catch (within another async function).
This is what you're currently doing:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc("some file");
console.log(res);
You need to be doing either this:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
(async() => {
try {
let res = await myfunc("some file");
console.log(res);
} catch (e) {
// Deal with the fact there was an error
}
})();
or with then and catch:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
myfunc("some file")
.then(res => {
console.log(res);
})
.catch(e => {
// Deal with the fact there was an error
});
convertFiletoPDF()
This function run and returned a Promise. This is fine.
myfunc()
Lets say myfunc takes 10 seconds. Javascript starts to wait newly created thread result from libuv via event loop mechanism. So, Javascript says, "That one is async, I will not wait, when it finishes it will let me know and i will run my then callback and then I will proceed with its output."
Javascript keeps his promise. Tries to run next below lines. myFunch is still working. Output is not ready yet. Returns undefined.
let res = myfunc(file);
console.log(res);
You get undefined.
Someone might find this example from my code useful. You can wrap it in a promise and then resolve the custom promise and then call another promise to confirm the receipt of the original web3 call.
return new Promise((resolve, reject) => {
tokenContract.methods.approve(
exchangeAddress,
BIG_NUMBER_1e50
)
.send({ from })
.once('transactionHash')
.once('receipt', receipt => resolve(receipt))
.on('confirmation')
.on('error', err => reject(err))
.then( receipt => // will be fired once the receipt its mined
console.log(receipt),
);
});