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.
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 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
I'd like to use the GeoPackage library using Promises, rather than Node-style callbacks.
Using promisify-node doesn't work:
const npromisify = require('promisify-node');
const geopackage = npromisify('#ngageoint/geopackage');
geopackage.openGeoPackage('data.gpkg').then((gpkg) => {
return npromisify(gpkg.getFeatureTables)();
}).then(tables => {
console.log(tables);
}).catch(console.error);
Somehow the this is not set correctly:
TypeError: this.getGeometryColumnsDao is not a function
at GeoPackage.getFeatureTables (/Users/stevebennett/odev/freelancing/crc-si/ziggurat/node_modules/#ngageoint/geopackage/lib/geoPackage.js:194:18)
The way that library function is defined seems normal enough:
GeoPackage.prototype.getFeatureTables = function (callback) {
var gcd = this.getGeometryColumnsDao();
gcd.isTableExists(function(err, exists) {
if (!exists) {
return callback(null, []);
}
gcd.getFeatureTables(callback);
});
};
The value of this inside that function is an object, but I can't tell what it is exactly. It's not the GeoPackage instance that the function body is expecting, in any case.
Is there a way to Promisify this type of library?
(I tried a couple of alternatives, such as Node's native util.promisify and a random Gist, but they made no difference.)
You can try bluebird. There is option of PromisifyAll, which will promisify your whole library:
http://bluebirdjs.com/docs/api/promise.promisifyall.html
Here is example of promisifyall mysql library:
const connection = mysql.createConnection({.....});
global.db = Bluebird.promisifyAll(connection);
db.queryAsync("SELECT * FROM users").then(function(rows){
console.log(rows);});
I'm trying to run a specific function from an existing app via AWS Lambda, using the JS SDK to invoke Lambda from my node.js app. Since I'm overwriting the existing function, I'll have to keep its basic structure, which is this:
overwrittenFunction = function(params) {
//get some data
return dataArray;
}
..so I need to end up with an array that I can return, if I'm looking to keep the underlying structure of the lib I use the same. Now as far as I know, Lambda invocations are asynchronous, and it's therefore not possible to do something like this:
overwrittenFunction = function(params) {
lambda.invoke(params, callback);
function callback(err,data) {
var dataArray = data;
}
return dataArray;
}
(I've also tried similar things with promises and async/await).
afaik I have two options now: somehow figure out how to do a synchronous Lambda invocation, or modify my library / existing app (which I would rather not do if possible).
Is there any way to do such a thing and somehow return the value I'm expecting?
(I'm using node v8.9.4)
Lambda and async/await are a bit tricky, but the following is working for me (in production):
const lambdaParams = {
FunctionName: 'my-lambda',
// RequestResponse is important here. Without it we won't get the result Payload
InvocationType: 'RequestResponse',
LogType: 'Tail', // other option is 'None'
Payload: {
something: 'anything'
}
};
// Lambda expects the Payload to be stringified JSON
lambdaParams.Payload = JSON.stringify(lambdaParams.Payload);
const lambdaResult = await lambda.invoke(lambdaParams).promise();
logger.debug('Lambda completed, result: ', lambdaResult.Payload);
const resultObject = JSON.parse(lambdaResult.Payload)
Wrap it all up in a try/catch and go to town.
You can use async await but as the AWS SDK uses node callback pattern you'll need to wrap the function with the built-in promisify.
const promisify = require('utils').promisify
const aws = require('aws-sdk');
const lambda = aws.Lambda();
const invoke = promisify(lambda.invoke);
async function invocation(params) {
try {
return await invoke(params);
} catch (err) {
throw new Error('Somethings up');
}
}
const data = invocation(params);