Make async code wait for end before executing - node.js

I have an async function like the one below. Note that the timeout function is just symbolic of other code that runs in an async way. I want the code to wait till each of the series block is done before returning to the final function.
var async = require('async')
var param1 = 'foobar'
function withParams(param1, callback) {
console.log('withParams function called')
console.log(param1)
callback()
}
function withoutParams(callback) {
if(true){
console.log("withoutParams function called")
setTimeout(function () {
console.log("test") }, 10000);
callback()
}
else{
console.log('This part will never run')
callback()
}
}
async.series([
function(callback) {
withParams(param1, callback)
},
withoutParams
], function(err) {
console.log('all functions complete')
})
The output is
withParams function called
foobar
withoutParams function called
all functions complete
test
I want the output to be
withParams function called
foobar
withoutParams function called
test
all functions complete
Is there any other async version that waits for the final block to finish before calling the ending function(err){... part ? I am learning nodejs.

You need to just move your callback:
function withoutParams(callback) {
if(true){
console.log("withoutParams function called")
setTimeout(function () {
console.log("test")
callback() // <-- HERE
}, 1000);
}
else{
console.log('This part will never run')
callback()
}
}
Of course you can get the same results without the async library by just using promises:
var param1 = 'foobar'
function withParams(param1) {
console.log('withParams function called')
console.log(param1)
}
function withoutParams() {
return new Promise((resolve, reject) => {
if (true) {
console.log("withoutParams function called")
setTimeout(function() {
console.log("test")
resolve()
}, 1000);
} else {
console.log('This part will never run')
reject()
}
})
}
withParams(param1)
withoutParams()
.then(() => console.log('all functions complete'))

Related

Node async function callback

I am looking at how to chain async functions in node.
Using callbacks I could simply have:
function doThis(callback) {
step1;
step2;
step3;
callback();
}
I could then nest this function with:
dothis(function() {
console.log("Done once");
doThis(function() {
console.log("Done twice");
});
});
I am now working with a try catch async function and wish to achieve the same outcome:
async function doThis() {
try {
step1;
step2;
step3;
} catch (error) {
console.error(error);
}
}
I can call this function with:
await doThis();
However, I wish to call it twice synchronously as per the callback above?

async/await not working properly with fs.writeFile

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?
}

NodeJS Elasticsearch search returns promise instead of values

I made a request inside my Node server to Elasticsearch. This is working perfect, except that I always get a promise returned instead of the results.
When I console log the results they look perfect, but when I return them I either get nothing or a promise.
Can someone tell me the proper way to retrieve, and handle the data from Elasticsearch?
I am using VueJS with a Node server and the official Elasticsearch package.
function getNewTest(client)
{
client.search({
index: 'myIndex',
}).then(function(resp) {
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
let tests = getNewTest(client);
console.log(tests);
# Output: Promise { <pending> }
EDIT:
As suggested I tried both codes, both didnt work. I changed my own code, now it returns an "undefined" to me.
getNewTest(client).then(function (response) {
console.log(response);
});
will return "undefined" to me. I changed my function to this:
async function getNewTest(client)
{
await client.search({
index: 'myIndex',
}).then(function(resp) {
console.log(resp.hits.hits, 'returned');
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
When I would do
let test = getNewTest(client);
it returns a promise to me.
(async () => {
let tests = await getNewTest(client);
console.log(tests);
})();
You are making a db call, so the main thread becomes free and start executing the next line. The code needs to wait till the promise is resolved and then execute the next line.
Or if you dont want to use async await, you can use this code below -
async function getNewTest(client) {
client.search({
index: 'myIndex',
}).then(function (resp) {
return resp.hits.hits;
}, function (err) {
console.trace(err.message);
});
}
getNewTest(client).then(result => {
console.log(result);
});
Function getNewTest will alway return undefined because you do not explicitly return anything.
Even if you did it this way :
function getNewTest(client)
{
return client.search({
index: 'myIndex',
}).then(function(resp) {
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
It will return a promise.
const generatePromise = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1500)
}, 500)
})
function test () {
return generatePromise()
.then((data) => console.log('after promise resolved ', data))
.catch((err) => console.log(err))
}
console.log('before calling test function');
const result = test()
console.log('after calling test function', result instanceof Promise);
When we call a function that returns a promise (async work) the execution does not wait for the promise to resolve it continue executing other code, that why const result = test() won't have the result of the promise.
The result of the promise will only be available inside the then handler as you can see in the code snippet.
const generatePromise = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1500)
}, 500)
})
function test () {
return generatePromise()
}
console.log('before calling test function');
test()
.then((data) => console.log('after promise resolved ', data))
.catch((err) => console.log(err))
console.log('after calling test function');
You can achieve the required using async & await:
const generatePromise = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1500)
}, 500)
})
// async function always returns promise even if you did't explicitly do
async function test () {
const data = await generatePromise();
// you can access resolved data here
console.log('after promise resolved ', data);
return data;
}
// first without then
console.log('before calling test function');
// you can't access it here unless you use then
// test().then(data => console.log(data));
const result = test();
console.log('after calling test function', result instanceof Promise);
This is how asynchronous works you can't return a promise and expect to receive the results instantly you can access the results inside the then handles or use await as I did.
When you do this:
async function getNewTest(client)
{
await client.search({
index: 'myIndex',
}).then(function(resp) {
console.log(resp.hits.hits, 'returned');
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
it means this:
async function getNewTest(client)
{
await client.search({
index: 'myIndex',
}).then(function(resp) {
console.log(resp.hits.hits, 'returned');
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
return undefined; // you are deliberately returning undefined
}
Remember that in javascript if you don't return anything the result of the function is undefined. I'm guessing what you intend to do is:
async function getNewTest(client)
{
return await client.search({ // NOTE THIS LINE
index: 'myIndex',
}).then(function(resp) {
console.log(resp.hits.hits, 'returned');
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
Since .search() already returns a Promise (or promise-like object) you don't need to await for it. The code above is exactly the same as:
function getNewTest(client)
{
return client.search({ // NOTE THIS RETURN
index: 'myIndex',
}).then(function(resp) {
console.log(resp.hits.hits, 'returned');
return resp.hits.hits;
}, function(err) {
console.trace(err.message);
});
}
But still, this does not allow you to do let test = getNewTest(client). Nothing will ever make this possible. It is simply impossible. To get the result of getNewTest() either call it's .then() method or await for it. In other words, either do this:
getNewTest(client).then(function(test) { /*continue logic here*/ })
or do this:
async function foo () {
let test = await getNewTest(client);
/*continue logic here*/
}
foo();
Note that this mechanism applies everywhere. It is also impossible to do this:
async function foo () {
let test = await getNewTest(client);
return test;
}
let test = foo();
You must instead do this if you want to go this route:
async function foo () {
let test = await getNewTest(client);
return test;
}
async function bar () {
let test = await foo();
/*continue logic here*/
}
There is no escape. You can never directly return an asynchronous value.

Nodejs run task in sequence

I am new to node.js and I just don't know how to execute a settimeout function before another function,
for example,
var async = require('async');
function hello(){
setTimeout(function(){
console.log('hello');
},2000);}
function world(){
console.log("world");
}
async.series([hello,world()]);
and the output is always world hello.
Am I using the library right? I dont the question seems trivial but I really have no idea how to force a short task to run after a long one
Async requires you to use callback. Follow this link to see some examples. The following code should output hello world correctly:
var async = require("async");
function hello(callback) {
setTimeout(function(){
console.log('hello');
callback();
}, 2000);
}
function world(callback) {
console.log("world");
callback();
}
async.series([hello, world], function (err, results) {
// results is an array of the value returned from each function
// Handling errors here
if (err) {
console.log(err);
}
});
Note that callback() was called inside the setTimeout() function so that it waits for the console.log('hello').
Use promise
function hello(){
return new Promise(function(resolve, reject) {
console.log('hello');
resolve();
});
}
function world(){
return new Promise(function(resolve, reject) {
console.log("world");
resolve();
});
}
hello()
.then(function(){
return world()
})
.then(function(){
console.log('both done');
})
.catch(function(err){
console.log(err);
});
You can use promises instead of callbacks. so your code will be something like below:
var async = require('async');
function hello(){
setTimeout(function(){
console.log('hello');
},2000);}
function world(){
console.log("world");
}
return Q.fcall(function() {
hello();
})
.then(function(resultOfHelloFunction){
world();
});
The World() function will only be executed when hello() function is completed its execution.
P.S : I am using Q library to promisify functions. It's totally fine to use other libraries (such as bluebird) to achieve the same thing.
You can use callback which are the heart of the nodejs.
var fun1 = function(cb){
// long task
// on done
return cb(null, result);
}
var fun2 = function(cb){
return cb(null, data);
}
fun1(function(err, result){
if(!err){
fun2(function(er, data){
// do here last task
})
}
}
// to avoid pyramid of doom use native promises
func1 (){
return new Promise((resolve, reject) => {
// do stuff here
resolve(data)
})
}
func2(){
return new Promise((resolve, reject) => {
resolve(data);
})
}
And then call them using:
func1.then((data) => {
return func2();
})
.then((resule) => {
//do here stuff when both promises are resolved
})

NodeJS: How to use for async helper method for async method

I'm using Async library for asynchronous programming on NodeJS. I want to make a helper method (that also use async method), rather than put all code in same place.
Here is my sample code:
var solve = function() {
async.waterfall([
// a long working task. huh
function (callback) {
setTimeout(function() {
console.log('hello world!');
callback();
}, 2000);
},
function (callback) {
authorService.helperAsync();
},
function (callback) {
setTimeout(function() {
console.log('bye bye!');
}, 2000);
}
]);
};
solve();
In other file, I make an async function:
var helperAsync = function() {
async.waterfall([
function(callback) {
console.log('task 1');
callback();
},
function (callback) {
console.log('task 2');
callback();
}
]);
}
I want the result should be:
Hello World
Task 1
Task 2
Bye Bye
But the output is only
Hello World
Task 1
Task 2
. How can I fix my code ?
Thanks :)
You need to setup each file to be used as a module, which involves exporting the function that you'd like to return, like so:
solve.js
var async = require('async');
var helperAsync = require('./helperAsync.js');
module.exports = function solve() {
async.waterfall([
function helloOne(next) {
setTimeout(function() {
console.log('hello world!');
return next();
}, 2000);
},
function helperCall(next) {
helperAsync(params, function(){
return next();
});
},
function byeBye(next) {
setTimeout(function() {
console.log('bye bye!');
return next();
}, 2000);
}
], function(result){
return result;
});
};
helperAsync.js
var async = require('async');
module.exports = function helperAsync (params, callback) {
async.waterfall([
function(next) {
console.log('task 1');
return next();
},
function (next) {
console.log('task 2');
return next();
}
], function(result){
return callback(result);
});
};
I've renamed the callback from your async.waterfall to next in order to prevent confusion with the main callback of the module.

Resources