New to NodeJS - is it possible to await a callback function? - node.js

What if I have a code like this:
var mysql = require('mysql');
var pool = mysql.createPool(...);
pool.getConnection(function(err, connection) {
if (err) throw err; // not connected!
// Use the connection
connection.query('SELECT something FROM sometable', function (error, results, fields) {
// When done with the connection, release it.
connection.release();
// Handle error after the release.
if (error) throw error;
// Don't use the connection here, it has been returned to the pool.
});
});
The logic from initializing the pool until the entire getConnection process is located in an async function. So what happens is, when deployed in AWS, logs in getConnection are not logged because the handler finishes execution before the callback is processed. Due to this, the logs from line "if (err) throw err" is not seen in AWS cloudwatch logs.
Is there a workaround for this? I simply want to await the callback function in pool.getConnection
I did my research but all I found were specific implementations for specific scenarios, none quite similar to my scenario.

is it possible to await a callback function?
No, not with a plain callback that does not use promises.
await only does something useful when you're awaiting a promises. The connection.query() function call you show in your question does NOT use promises so there is no place for await with that function call as it is.
But, you can either wrap the connection.query() in a function that returns a promise (referred to as promisifying it) and create a new function that is promise-based.
Here's a look at how promisifying works. You don't show the overall context of your code. The promisifying would more typically be done at a more centralized location, but I've shown it here in the only context that you've shown:
const { promisify } = require('util');
async function someFunc() {
const pool = mysql.createPool(...);
pool.getConnectionP = promisify(pool.getConnection);
let connection;
try {
connection = await pool.getConnectionP();
connection.queryP = promisify(connection.query);
const results = await connection.queryP('SELECT something FROM sometable');
return results;
} catch(e) {
console.log(e);
// handle errors here
} finally {
if (connection) {
connection.release();
}
}
}
Or, your database already has a promise-interface and you can just use that (which would be my recommendation). See the mysql2/promise interface here where these functions are already converted to use promises and you can then use await with them.

Related

Node js await is not working while connecting to postgres

I have created a saperate js file in node js and I am trying to connect to postgres sql using below code but it never worked. always promise is pending. code is not waiting at this line (await client.connect()). I Could not understand what the issue is. can any one please help me on this.
const pg = require('pg');
async function PostgresConnection(query,config) {
const client = new pg.Client(config);
await client.connect(); //not waiting for the connection to succeed and returning to caller and exit
const pgData = await client.query(query);
await client.end();
return pgData;
}
async function uploadDataToPostgres(config) {
var query="select * from firm_data";
await PostgresConnection(query, config);
}
module.exports = {
uploadDataToPostgres
}
I am trying to call above method from other page
function ProcessData()
{
var result = uploadDataToPostgres(config);
}
When you invoke an async function from outside an async function, you need to treat it as a Promise. Change your invoker function to look like this:
function ProcessData() {
uploadDataToPostgres(config)
.then(function success(result) {
/* do something useful with your result */
console.log(result)
})
.catch(function error(err) {
console.error('uploadDataToPostgres failure', err)
})
}
Your code, var result = uploadDataToPostgres(config), stashes a Promise object in result. But the code behind the promise (the code in your async function) doesn't run until you invoke .then() or .else() on it.
It's a bit confusing until you completely grasp the similarity between async functions and Promises: async functions are Promise functions with nice syntax.
It's possible your uploadDataToPostgres() function throws an error (failure to connect to the db?) and the .catch() will catch that.

NodeJS - Properly catch errors of async functions

I was writing a script to pull data from Google Cloud metrics via API when I accidentally discovered that I don't know how to properly catch errors of asynchronous functions. :O
Here is the example code from google cloud:
// Imports the Google Cloud client library
const monitoring = require('#google-cloud/monitoring');
// Creates a client
const client = new monitoring.MetricServiceClient();
/**
* TODO(developer): Uncomment and edit the following lines of code.
*/
const projectId = 'XXXXXXXXX';
async function getMetrics() {
const request = {
name: client.projectPath(projectId),
filter: 'metric.type="cloudsql.googleapis.com/database/cpu/utilization"',
interval: {
startTime: {
// Limit results to the last 20 minutes
seconds: Date.now() / 1000 - 60 * 1,
},
endTime: {
seconds: Date.now() / 1000,
},
},
// Don't return time series data, instead just return information about
// the metrics that match the filter
view: 'HEADERS',
};
// Writes time series data
console.log('start')
const [timeSeries] = await client.listTimeSeries(request);
console.log('Found data points for the following instances:');
timeSeries.forEach(data => {
console.log(data.metric.labels.instance_name);
});
}
getMetrics();
The function listTimeSeries returns a promise. I got an error that I need to be authenticated to perform that action, no problem there.
The issue is that I couldn't catch that error.
I tried surrounding the call with try {...} catch (err) {...} block, wasn't caught.
I tried to catch it like this const [timeSeries] = await client.listTimeSeries(request).catch(console.log); - No luck there.
I must be missing something because I'm pretty new to nodeJS and no way catching errors from async functions is not supported.
I'm using nodeJS v14.
What am I missing guys?
Thank you in advance!
EDIT
As requested (by #CherryDT), here is the full error output:
I hope its not too blurry.
EDIT
It turns out that the way I've been trying to catch errors is fine.
The issue occurred because of listTimeSeries function (from an external library), which threw an error instead of rejecting the promise, which is impossible to catch.
Thanks, guys.👍
Note that I refer to "async functions" and "asynchronous functions." In Javascript "async function" means a function created with the async keyword, whereas when I say "asynchronous function" I mean in the traditional sense, any function that runs asynchronously. In Javascript, functions created with the async keyword are actually just promises under the hood.
Your code would work if errors thrown from asynchronous functions (inside promises) could be caught. Unfortunately, they can't. Unless the function is using the async function syntax, errors in promises must be wrapped with reject. See the MDN example for the gotcha we're looking at here:
// Throwing an error will call the catch method most of the time
var p1 = new Promise(function(resolve, reject) {
throw new Error('Uh-oh!');
});
p1.catch(function(e) {
console.error(e); // "Uh-oh!"
});
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw new Error('Uncaught Exception!');
}, 1000);
});
p2.catch(function(e) {
console.error(e); // This is never called
});
// Errors thrown after resolve is called will be silenced
var p3 = new Promise(function(resolve, reject) {
resolve();
throw new Error('Silenced Exception!');
});
p3.catch(function(e) {
console.error(e); // This is never called
});
I believe this is the code in the library that's throwing the error, below. Notice that another error is being properly rejected. All comments are mine.
for (const methodName of metricServiceStubMethods) {
const callPromise = this.metricServiceStub.then(
stub => (...args: Array<{}>) => {
if (this._terminated) {
// This is the right thing to do!
return Promise.reject('The client has already been closed.');
}
const func = stub[methodName];
return func.apply(stub, args);
},
(err: Error | null | undefined) => () => {
// If this was an async function (as in, using the keyword async,
// not just literally an asynchronous function), this would work,
// because the async keyword is just syntactic sugar for creating
// a promise. But it's not so it can't be caught!
throw err;
}
);
I believe, in this case, unfortunately there's no way for you to catch this error.
You can do this.
(async function() {
try {
await getMetrics();
} catch(error) {
console.log("Error occured:", error);
}
})();
Please note that if you are trying to catch the error in Promise you can use .then(() => { }).catch(err => { }) style, but for async/await you will need try { } catch(err) { } style to catch the error.
Edit
By doing this, it must catch any errors if the promise become rejected. If you still cannot catch the error, this means that the library you're using doesn't reject the promise properly (Promise.reject()), instead it did hard-coded throw error inside the promise instead of rejecting one. For this case you can't do anything with error catching.

Why my lambda is working only when i give a callback with some message?

I am trying to run a small snippet of lambda code where i am pushing data to S3 using firehose. Here is my snippet
const AWS = require( 'aws-sdk' );
var FIREhose = new AWS.Firehose();
exports.handler = async (event,context,callback) => {
// TODO implement
const response = {
statusCode:200,
Name:event.Name,
Value:event.Value
};
const params = {
DeliveryStreamName: 'kinesis-firehose',
Record: { Data: new Buffer(JSON.stringify(response)) }
};
FIREhose.putRecord(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data);
});
};
Here are my events
{
"Name": "Mike",
"Value": "66"
}
When i run this lambda all i am getting response as null . Since i am not passing any callback lambda will default run the implicit callback and returns null. I see that no data is pushed to S3 bucket.
But when i add callback(null,"success") line at the end like this
FIREhose.putRecord(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data);
});
callback(null,"success")
};
I see the data is pushed to S3. Why is that ?
Does async functions always need a callback with some text appended to it ?
Any help is appreciated ?
Thanks
The problem here is that you're mixing your node.js lambda patterns.
Either you use an asynchronous function and return or throw:
exports.handler = async (event,context,callback) => {
// code goes here.
await FIREhose.putRecord(params).promise();
return null; // or whatever result.
};
Or you use the callback approach:
exports.handler = (event,context,callback) => {
// code goes here.
FIREhose.putRecord(params)
.promise();
.then((data) => {
// do stuff with data.
// n.b. you could have used the cb instead of a promise here too.
callback(null, null); // or whatever result.
});
};
(There's a third way using context. but that's a very legacy way).
This is all due to how lambda works and detects when there's been a response.
In your first example (no callback), lambda is expecting your handler to return a promise that it has to wait to resolve/reject, which, in turn, will be the response. However, you're not returning a promise (undefined) and so there's nothing to wait for and it immediately returns- quite probably before the putRecord call has completed.
When you used callback though, you explicitly told lambda that you're using the "old" way. And the interesting thing about the callback approach is that it waits for node's event loop to complete (by default). Which means that .putRecord will probably complete.

await inside async controller function [duplicate]

I am working on a simple TCP client for a server and am using the latest node 7.6 because of the async/await functions. I'm new to node and asynchronous coding, so I apologize if this is stupidly easy.
I want to run a function that calls the callServer() function with specific parameters, wait until it finishes getting the data, and return the data as a variable.
Here is my code:
'use strict'
const net = require('net')
var formattedJson = funcRequestToJson("Server.GetStatus", false)
doThings()
async function doThings() {
var info = await callServer()
}
async function callServer() {
var client = new net.Socket()
client.connect(1705, '192.168.28.16', () => {
console.log('connected to server')
client.write(formattedJson)
})
client.on('data', (data) => {
client.destroy()
//return await data
console.log(data.toString())
})
client.on('close', () => {
})
}
// method and paramBool are always required
// macAddress, paramKey, paramValue are required for if paramBool is true
function funcRequestToJson(method, paramBool, macAddress, paramKey, paramValue) {
var objectRequest = {}
objectRequest[ "jsonrpc" ] = "2.0"
objectRequest[ "method" ] = method
objectRequest[ "id" ] = 0
if (paramBool == true) {
objectRequest[ "params" ] = {
"client": macAddress,
[paramKey]: paramValue
}
}
var json = (JSON.stringify(objectRequest) + '\r\n')
return json
}
So I didn't declare objectRequest() as async because it's not waiting on the server, but I think callServer() should be async, right? I know this can be done with promises, but I wanted to use async/await and this seems to be right.
Now, I want to return the data that comes from inside callServer() and client.on('data', (data) but I can't seem to figure out how to do it asynchronously. I would think there'd be a way to make an async function and call it with await like I tried (await return data) but it never works right.
I'm sorry if this is terribly convoluted, but I've been poring over async node tutorials for the past week and am still stuck.
Thanks!
Async/Await relies on code that uses promises for async operations. So you need to return a promise from any async operation in order to use it with async/await.
So, callServer() needs to return a promise that is resolved when the async operation inside it is done. In fact, you can only await an async operation in a function if that function returns a promise. await saves you from having to write .then() handlers on promises, but async/await does not magically know when async operations are done. You still have to wrap them in promises.
Here's an example of how you could make callServer() return a promise:
async function callServer(formattedJson) {
return new Promise((resolve, reject) => {
let client = new net.Socket()
client.connect(1705, '192.168.28.16', () => {
console.log('connected to server')
client.write(formattedJson)
})
client.on('data', (data) => {
resolve(data);
client.destroy()
})
client.on('close', () => {
})
client.on('error', reject);
});
}
Sample Usage:
try {
var info = await callServer(funcRequestToJson("Server.GetStatus", false));
} catch(e) {
// error here
}
Just to show you what await is doing, in ES5 (without async/await), the sample usage would be this:
callServer(funcRequestToJson("Server.GetStatus", false)).then(info => {
// info is available here
}).catch(err => {
// error here
});
So, await is just letting you avoid writing the .then() handler. Internally in the execution of the code, there's just a promise that the interpreter sets up a .then() handler for so it will know when that promise is resolved. This is syntactical sugar, not really any magic with async operations. You still have to use promises for all your async operations. You can use await instead of .then() and your code will appear more sequential even though it's really the same under the covers.
People think that await allows one to write asynchronous code as if it was synchronous, but that isn't really the case, especially if you handle errors and understand concurrency issues. It will look more synchronous, but isn't actually any more synchronous than it was with .then() in ES5.
And, watch out for error handling. In people's quest to write synchronous looking code, people seem to completely forget to handle rejected promises in their async operations (which are handled with try/catch when using await). I'm personally not yet convinced that ES6 is a step forward when it comes to error handling as the early indications are that ES6 seems to encourage people to just forget about error handling or get lazy and not do it, whereas it's easier when using .then() to just know that there should be a .catch() somewhere for it to be solid code. Maybe that's just a learning process, but it seems to be an early issue when people use await.

Bluebird promisify and callback with no error argument

I'm trying to promisify a 3rd party library that doesn't use the callback(err, data) pattern. Instead they always return callback(data) and throw on errors.
Promise.promisifyAll(horse);
var p = Promise.defer();
horse.drinkAsync()
.error(function(data)
{
p.fulfill(data);
})
.catch(function (err)
{
console.error('error occured', err);
});
return p.promise;
What is a nice way to wrap such a behavior with promises and still have it look ok and allow to catch the thrown error? The catch clause doesn't trigger and the application crashes.
From Bluebird 2.1 on, you can now customize promisifyAll with a custom promisification handler:
function noErrPromisifier(originalMethod){
return function promisified() {
var args = [].slice.call(arguments); // might want to use smarter
var self = this // promisification if performance critical
return new Promise(function(resolve,reject){
args.push(resolve);
originalMethod.apply(self,args); // call with arguments
});
};
}
var horse = Promise.promisifyAll(require("horse"), {
promisifier: noErrPromisifier
});
horse.drinkAsync().then(function(data){
// Can use here, ow promisified normally.
});
If the original method throws asynchronously, there is really no way around wrapping it in a domain, although I've never seen a library that acts that poorly.

Resources