I have been trying to use the new async/await feature of Node 8 with node-mysql module. Have been partial successful in incorporating async/await with mysql. When I create a cluster pool connection with node-mysql client I am unable to do this without the callback. If I write callback I am able to get the data from MySQL but when I am using async/await I am getting weird errors. Although I have been able to write the getConnection function in async/await form but no luck with the connection.query part. Can anybody help in this context ?
SAMPLE CODE
var poolCluster = mysql.createPoolCluster();
var masterConfig = {...};
var slaveConfig1 = {...};
poolCluster.add('MASTER', masterConfig);
poolCluster.add('SLAVE1', slaveConfig1);<br>
poolCluster.getConnection = util.promisify(poolCluster.getConnection());<br>
module.exports.PoolCluster = poolCluster;<br>
//connect-cluster-pool.js
var poolClusterSrv = require('../pool-cluster');<br>
async fetchRecord() {<br>
let queryStr = 'select * from customers';<br>
console.log("inside fetchRecord()...");<br>
try {<br>
let connection = await <br>poolClusterSrv.PoolCluster.getConnection('SLAVE1'); --<b>this is working</b> <br>
console.log("Connected to SLAVE1::", connection.threadId);
//getting new threadId every time
let results = await util.promisify(connection.query(queryStr));
//this is not working / working only with callback</b> <br>
console.log(results);<br>
return results;<br>
}<br>
catch (error) {<br>
console.log("error in fetchRecord()::", error);<br>
}<br>
}<br>
util.promisify should work on any function that uses Node-style, error-first callbacks (unless there is code that relies on function arity, which is rare). If a function is a method that relies on the context, it should be executed with same context as original one.
util.promisify(poolCluster.getConnection()) and util.promisify(connection.query(queryStr)) are mistakes. It's a function and not its result that should be promisified.
Instead, it should be:
poolCluster.getConnection = util.promisify(poolCluster.getConnection);
...
connection.query = util.promisify(connection.query);
let results = await connection.query(queryStr);
...
I think your problem is that query is a method, not a function. It has this bound in context and your promisify loses it. I don't know what your promisify implemention is, but pify supports promisifying the whole object and then call the method. Alternatively:
await new Promise((resolve, reject) => connection.query(..., (error, result) => error ? reject(error) : resolve(result)))
EDIT:
Use this package instead of mysql: https://www.npmjs.com/package/promise-mysql
Related
I'm creating my own package that needs to do some emitting from the package to the main file.
This simplified module gets what I'm trying to achieve:
const fetch = require("node-fetch")
module.exports.client = async function(username,password,version) {
// Do login code here
this.emit("login", loginData)
}
Then my test code runs that with
var client = new require("../index").client("username","password","0.774")
client.on("login",function(data) {
console.log(data)
})
With every time, the program returning
client.on("login",function(data) {
^
TypeError: client.on is not a function
Has anyone had a similar issue? This is my first time making a module in this style, so please let me know.
Thank you!
Because your function client() exported from index.js is declared with the async keyword, it will return a Promise rather than an actual value passed to the return statement. Assuming the value you return from the function client() is an object with an on() function, then in order to access this function you either need to await the result of client() or you need to use the .then() function on promise. Example:
var clientPromise = new require("../index").client("username","password","0.774")
clientPromise.then(client => {
client.on("login",function(data) {
console.log(data)
})
});
While you could also use await on the result of the client() function, this is not allowed in all contexts, and so you would have to wrap the code that does this in another async function.
it's the first time for me using async/await. I've got problems to use it in the context of a database request inside a dialogflow intent. How can I fix my code?
What happens?
When I try to run use my backend - this is what I get: "Webhook call failed. Error: Request timeout."
What do I suspect?
My helper function getTextResponse() waits for a return value of airtable, but never get's one.
What do I want to do?
"GetDatabaseField-Intent" gets triggered
Inside it sends a request to my airtable database via getTextResponse()
Because I use"await" the function will wait for the result before continuing
getTextResponse() will return the "returnData"; so the var result will be filled with "returnData"
getTextResponse() has finished; so the response will be created with it's return value
'use strict';
const {
dialogflow
} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});
const Airtable = require('airtable');
const base = new Airtable({apiKey: 'MyKey'}).base('MyBaseID');
///////////////////////////////
/// Helper function - reading Airtable fields.
const getTextResponse = (mySheet, myRecord) => {
return new Promise((resolve, reject) => {
// Function for airtable
base(mySheet).find(myRecord, (err, returnData) => {
if (err) {
console.error(err);
return;
}
return returnData;
});
}
)};
// Handle the Dialogflow intent.
app.intent('GetDatabaseField-Intent', async (conv) => {
const sheetTrans = "NameOfSheet";
const recordFirst = "ID_OF_RECORD";
var result = await getTextResponse(sheetTrans, recordFirst, (callback) => {
// parse the record => here in the callback
myResponse = callback.fields.en;
});
conv.ask(myResponse);
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
As #Kolban pointed out, you are not accepting or rejecting the Promise you create in getTextResponse().
It also looks like the var result = await getTextResponse(...) call is incorrect. You have defined getTextResponse() to accept two parameters, but you are passing it three (the first two, plus an anonymous arrow function). But this extra function is never used/referenced.
I would generally avoid mixing explicit promises with async/await and definitely avoid mixing async/await with passing callbacks.
I don't know the details of the API you are using, but if the API already supports promises, then you should be able to do something like this:
const getTextResponse = async (mySheet, myRecord) => {
try {
return await base(mySheet).find(myRecord)
}
catch(err) {
console.error(err);
return;
}
)};
...
app.intent('GetDatabaseField-Intent', async (conv) => {
const sheetTrans = "NameOfSheet";
const recordFirst = "ID_OF_RECORD";
var result = await getTextResponse(sheetTrans, recordFirst)
myResponse = result.fields.en;
conv.ask(myResponse);
});
...
Almost all promised based libraries or APIs can be used with async/await, as they simply use Promises under the hood. Everything after the await becomes a callback that is called when the awaitted method resolves successfully. Any unsuccessful resolution throws a PromiseRejected error, which you handle by use of a try/catch block.
Looking at the code, it appears that you may have a misunderstanding of JavaScript Promises. When you create a Promise, you are passed two functions called resolve and reject. Within the body of your promise code (i.e. the code that will complete sometime in the future). You must invoke either resolve(returnData) or reject(returnData). If you don't invoke either, your Promise will never be fulfilled. Looking at your logic, you appear to be performing simple returns without invoking resolve or reject.
Let me ask you to Google again on JavaScript Promises and study them again with respect to the previous comments just made and see if that clears up the puzzle.
I am trying to get the value of variable w_name outside query even I have defined w_name before query but cannot getting it after query. Please check the code and help:
connection.query('SELECT * FROM workstations WHERE id=?',[results[0].w_id],function(err,res,field){
w_name = res[0].name;
});
console.log(w_name);
console.log showing undefined but if I am putting console.log inside the query after w_name it is showing proper result. What's wrong in it?
connection.query(...params) is asynchronous function, which requires some time to read and get resulting data for query passed to it.
Defining console.log(w_name) prints undefined because result is not yet assigned to w_name variable. Whereas defining it inside callback function assigns value to w_name.
Check this article for understanding asynchronous javascript.
connection.query('SELECT * FROM workstations WHERE id=?',
[results[0].w_id],function(err,res,field){
w_name = res[0].name;
console.log(w_name);
});
NodeJs is asynchronous, It uses callback for the results.
The best solution as far as i know about your problem is given below:
async function myQuery(){
return new Promise((resolve,reject)=>{
connection.query('SELECT * FROM workstations WHERE id=?',[results[0].w_id],function(err,res,field){
resolve(res[0].name)
});
})
}
Now you can use then to get the value or use async await to get the value like this.
//this is where you would like to call that function.
async function index(){
let w_name = await myQuery()
console.log(w_name)
}
here you can use Promise() and resolve() the query of the result.
const w_name = await new Promise(function(resolve, reject) {
connection.query('SELECT * FROM workstations WHERE id=?', [results[0].w_id], function(err, res, field) {
if (err) throw err
resolve(res[0].name);
})
});
console.log(w_name);
I am trying to use util.promisify to convert AWS Document client get function to a promise based utility. But it does not seem to behave as expected;
// This does not work as expected
const docClient = new AWS.DynamoDB.DocumentClient();
let docClientGet = require('util').promisify(docClient.get);
However when i do usual promise conversion like this,
let docClientGet = function (params) {
return new Promise((resolve, reject) => {
docClient.get(params, function (err, data) {
if (err) {
return reject(err);
}
return resolve(data);
})
})
};
And use it in an async function like this:
await docClientGet(params);
It works!.
I wonder where I am wrong in understanding util.promisify
If the method you are promisifying needs to be associated with the object it is on (which it looks like it does in your case, then this code:
let docClientGet = utils.promisify(docClient.get);
will not retain the association with the docClient object. What happens is that the promisified docClient.get() gets called without the this value set to the docClient object and it can't do its job properly.
You can work around that with this:
utils.promisify(docClient.get.bind(docClient));
The promisify doc does not make this clear because it uses an example from the fs library whose methods do not need to be associated with the fs object in order to work properly.
P.S. It's a bit unusual to put the util library into a variable named utils. That is likely to confuse some people reading your code.
I want to promisify node-postgres' pg.connect method along with the inner connection.query method provided in the callback.
I can .promisify the latter, but I need to implement the first one manually (if I'm missing something here, please explain).
The thing is, I'm not sure if this code is correct or should be improved? The code is working, I just want to know if I'm using Bluebird as meant.
// aliases
var asPromise = Promise.promisify;
// save reference to original method
var connect = pg.connect.bind(pg);
// promisify method
pg.connect = function (data) {
var deferred = Promise.defer();
connect(data, function promisify(err, connection, release) {
if (err) return deferred.reject(err);
// promisify query factory
connection.query = asPromise(connection.query, connection);
// resolve promised connection
deferred.resolve([connection,release]);
});
return deferred.promise;
};
Throw all that horrible callback code away, then do this somewhere in your application initialization:
var pg = require("pg");
var Promise = require("bluebird");
Object.keys(pg).forEach(function(key) {
var Class = pg[key];
if (typeof Class === "function") {
Promise.promisifyAll(Class.prototype);
Promise.promisifyAll(Class);
}
})
Promise.promisifyAll(pg);
Later in anywhere you can use the pg module as if it was designed to use promises to begin with:
// Later
// Don't even need to require bluebird here
var pg = require("pg");
// Note how it's the pg API but with *Async suffix
pg.connectAsync(...).spread(function(connection, release) {
return connection.queryAsync("...")
.then(function(result) {
console.log("rows", result.rows);
})
.finally(function() {
// Creating a superfluous anonymous function cos I am
// unsure of your JS skill level
release();
});
});
By now there are a number of libraries which do this for you:
pg-promise - generic Promises/A+ for PG
postgres-bluebird
dbh-ph
pg-bluebird
Update for bluebird 3:
The pg.connectAsync(...).spread(function(connection, release) { ... }) call will not work anymore, because the API of bluebird has changed: http://bluebirdjs.com/docs/new-in-bluebird-3.html#promisification-api-changes .
The problem is that promisifyAll in bluebird 3 does not handle multiple arguments by default. This results in the .spread() call reporting a TypeError like the following:
TypeError: expecting an array or an iterable object but got [object Null]
To solve this, you can explicitly enable multiple arguments for connect / connectAsync. Do the following after all the promisifying stuff mentioned above:
...
pg.connectAsync = Promise.promisify(pg.connect, { multiArgs: true });
I suggest to modify Petka Antonov solution a bit
var Promise = require('bluebird');
var pg = require('pg');
Object.keys(pg).forEach(function (key) {
var Cls = null;
try {
Cls = pg[key];
if (typeof Cls === 'function') {
Promise.promisifyAll(Cls.prototype);
Promise.promisifyAll(Cls);
}
} catch (e) {
console.log(e);
}
});
Promise.promisifyAll(pg);
here 'pg[key] wrapped up in try-catch block because pg[key] can retrun error when attempt to access pg['native']