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?
Related
Today, when I was working with node, I met some special async functions with "overloads" that accept both promises and callbacks. Like this:
doSomething(result => {
console.log(result)
})
doSomething()
.then(result => console.log(result))
And probably this:
const result = await doSomething()
console.log(result)
I tried to implement this in my code but was unsuccessful. Any help would be appreciated.
You can make a function like this by creating a promise, then chaining on that promise with the argument if there is one, and then returning that chained promise. That will make it call the callback at the appropriate time, as well as giving you access to the promise that will complete when the callback completes. If you want the original promise even when there's a callback (not the chained version), then you can return that instead, by still chaining but then returning the original promise instead.
function f(cb) {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), 1000);
});
if (cb) {
return promise.then(cb);
} else {
return promise;
}
}
// usage 1
f(console.log)
// usage 2
f().then(console.log)
Let's say you had a function that was going to read your config file and parse it and you wanted to support both versions. You could do that like this with two separate implementation inside. Note, this has full error handling and uses the nodejs calling convention for the callback that passes parameters (err, result):
function getConfigData(filename, callback) {
if (typeof callback === "function") {
fs.readFile(filename, function(err, data) {
try {
if (err) throw err;
let result = JSON.parse(data);
callback(null, result);
} catch(e) {
callback(err);
}
});
} else {
return fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
}).
}
}
This could then be used as either:
getConfigData('./config.json').then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
configData('./config.json', (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
Depending upon the specific asynchronous operation, it may be better or more efficient to have two separate implementations internally or to have one implementation that you adapt at the end to either a callback or a promise.
And, there's a useful helper function if you adapt a promise to a callback in multiple places like this:
function callbackHelper(p, callback) {
if (typeof callback === "function") {
// use nodejs calling convention for callbacks
p.then(result => {
callback(null, result);
}, err => {
callback(err);
});
} else {
return p;
}
}
That lets you work up a simpler shared implementation:
function getConfigData(filename, callback) {
let p = fs.promises.readFile(filename).then(data => {
return JSON.parse(data);
});
return callbackHelper(p, callback);
}
I am using AWS Amplify to build some backend fun in AWS, and I have a Lambda function that is triggered from an update to DynamoDB. It doesn't need to return anything.
I keep getting errors in CloudWatch saying "SyntaxError: await is only valid in async function". Is there another way to run these async functions within the handler?
exports.handler = async (event) => {
console.log(JSON.stringify(event, null, 2));
event.Records.forEach(record => {
console.log(record.eventID);
console.log(record.eventName);
console.log('DynamoDB Record: %j', record.dynamodb);
if (record.eventName == "INSERT") {
try {
res = await axios.get("https://google.com", {});
console.log(res);
}
catch(e){
console.log(e);
}
}
});
};
you are performing an await inside of a forEach loop which is it's own function state. You could try an inline async await function inside the if statement
(async function process() {
try {
res = await axios.get("https://google.com", {});
console.log(res);
}
catch(e){
console.log(e);
}
}())
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));
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'))
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
})