Async function in wit actions - node.js

I am currently developing a bot using wit.ai. I am quite new to node.js. Basically, I am following the guide provided by node-wit lib. I construct my wit object by:
const wit = new Wit({
accessToken: WIT_TOKEN,
actions,
logger: new log.Logger(log.INFO)
});
In my actions, I have something like:
const actions = {
send({sessionId}, {text}) {
//my sending action goes here.
},
firstaction({context, entities,sessionId}) {
var data = async_function();
context.receiver = data;
return context;
}
}
The issue is that whatever comes after async_function will be executed first. I tried to let async_function return a promise. However, this wouldn't work since whatever comes after my first action in node-wit library will be executed first without waiting for the context to return. I don't want to modify the node-wit library.
Any idea that would solve my issue is appreciated!

you can use async library for asynchronous call
https://caolan.github.io/async/docs.html
const async = require('async')
const actions = {
send({sessionId}, {text}) {
//my sending action goes here.
},
firstaction({context, entities,sessionId}) {
async.waterfall([function(callback) {
var d = async_function();
// if err pass it to callback first parameter
// return callback(err)
callback(null,d);
}], function(err, result) {
if(err) {
return err;
}
var data = result;
context.receiver = data;
return context;
})
}
}

Related

Function that return undefined in node.js. Inside the function I have mongoDB.connect

I tried to make function for my project in the service. This service need to check is user exists in my database, but my function(this function is checking) inside the class return undefined.
This is my code:
const express = require("express");
const mongoDB = require('mongodb').MongoClient;
const url = "here I paste url to my databse(everything is good here)";
class CheckService {
isUserExists(username) {
mongoDB.connect(url, (error, connection) => {
if (error) {
console.log("Error", '\n', error);
throw error;
}
const query = {name: username};
const db = connection.db("users");
const result = db.collection("users").find(query).toArray(
function findUser(error, result) {
if (error) {
throw error;
}
const arr = [];
if (result.value === arr.value) {
console.log(false);
connection.close();
return false;
} else {
console.log(true);
console.log(arr);
console.log(result);
connection.close();
return true;
}
});
console.log(result);
});
}
}
module.exports = new CheckService();
I imported my service to another service:
const checkService = require('./check.service');
After this, I invoked my function from my service like this:
console.log('function:',checkService.isUserExists(username));
I expected good result, but function doesn't return, that I want, unfortunately.
Please help!
There are two problems with your function
it doesn't return anything
toArray() is a promise, so your console.log probably just prints a promise.
Probably you're looking for something like this:
class CheckService {
async isUserExists(username) {
const connection = await mongoDB.connect(url);
const query = {name: username};
const db = connection.db("users");
const result = await db.collection("users").find(query).toArray();
connection.close();
return result.length !== 0;
}
}
You'd then invoke it with something like
await new CheckService().isUserExists(username);
Though, it's worth noting that you probably don't need toArray and instead could use findOne or even count() since all you care about is existence. I probably also wouldn't instantiate a new connection each time (but that's not super relevant)
2 things wrong here. Firstly the first comment is correct. You're only logging the result and not passing back to the caller. Secondly, a quick peek at the mongo docs shows that retrieval methods are promises. You need to make the function async and add awaits where needed.
Mongo:
https://www.mongodb.com/docs/drivers/node/current/fundamentals/crud/read-operations/retrieve/
Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

problem with making api request in node.js and getting the response in/to function caller

Ive spent a bit of time trying to understand this. I hope the answer is obvious and just show my lack of experience
My goal is to send API request to steam for various IDs of game mods and find the time_updated for each one, to put these all into an array and then find out which one most the most recently updated
I have got the code below, but its not quite doing what I want, I think I am just getting muddled over timings
My plan was to have a few different values in arrMODID = [], and to loop through each one, get the time_updated, push that to an array and for const result = await myfunction(); to be able to access the data in the modinfoArray
however that is returning an array with just [{test},{}] in it and is being fired before the function has put any data into the array
can anyone give me a shove in the right direction please
thank you
import request from 'request';
const myfunction = async function(x, y) {
var arrMODID = ["2016338122"];
var modinfoArray = []
var timeUpdated
for (const element of arrMODID) {
request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'http://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1',
body: 'itemcount=1&publishedfileids[0]=2016338122',
},
function(error, response, body){
var response = JSON.parse(body);
var myResponse = response.response.publishedfiledetails
myResponse.forEach(function(arrayItem) {
//console.log(arrayItem.time_updated)
timeUpdated = arrayItem.time_updated
//console.log(timeUpdated)
modinfoArray.push({"2016338122":arrayItem.time_updated})
console.log(modinfoArray) // only this log returns the added items
})
});
}
return ["test", modinfoArray];
};
// Start function
const start = async function(a, b) {
const result = await myfunction();
console.log(result); // this returns the empty array
}
// Call start
start();
You need to use an http request library that supports promises so you can await that inside your function. You cannot successfully mix promises and asynchronous operations like request.post() that uses plain callbacks because you can manage the control flow in a promise-like way with plain callbacks.
I'd suggest using the got() library. Also, the request() library has been deprecated and is not recommended for new code. If you absolutely wanted to stay with the request() library, you could use the request-promise module instead, but keep in mind that the request() library is in maintenance mode only (no new feature development) whereas this list of alternatives are all being actively developed.
Here's a runnable implementation using the got() library:
import got from 'got';
const myfunction = async function() {
const arrMODID = ["2016338122"];
const modinfoArray = [];
for (const element of arrMODID) {
const response = await got.post({
headers: { 'content-type': 'application/x-www-form-urlencoded' },
url: 'http://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1',
body: 'itemcount=1&publishedfileids[0]=2016338122',
}).json();
const myResponse = response.response.publishedfiledetails;
for (const arrayItem of myResponse) {
modinfoArray.push({ "2016338122": arrayItem.time_updated });
}
}
return ["test", modinfoArray];
};
// Start function
const start = async function() {
const result = await myfunction();
console.log(result);
return result;
}
// Call start
start().then(result => {
console.log("done");
}).catch(err => {
console.log(err);
});

AWS JavaScript/Nodejs Comprehend PII client isn't await-able?

I am using AWS Comprehend's "PII" detection tools to spot personally identifying information (PII) in arbitrary text submitted by the public.
This is for a Nodejs Lambda that I mean to run in our AWS account.
const {Comprehend} = require('#aws-sdk/client-comprehend');
const client = new Comprehend({ region: "us-east-2" });
module.exports.handler = async (event, _context) => {
const message = event.data;
var redactedMessage = await getRedactedMessage(message);
return redactedMessage;
}
const getRedactedMessage = async (message) => {
// the Comprehend PII system takes strings up to 4000 characters
// so we split it on word boundaries after 4000 chars or less.
var messageParts = message.match(/.{1,4000}(\s|$)/g);
var redactedString = "";
for (const part in messageParts) {
try {
const checkPart = {
Text : message,
LanguageCode : "en"
};
client.detectPiiEntities(checkPart, function (err, pii) {
console.log("Why does this come last?");
});
} catch (error) {
console.error(error);
} finally {
console.log("Why do I get here first?");
}
console.log("And here? Then suddenly...");
}
}
No matter what, the output is
Why do I get here first?
And here? Then suddenly...
Why does this come last?
I have littered this with every possible combination of "async" and "await" commands. I have rewritten the client.detectPiiEntities(checkPart, function (err, pii)... line like a promise with a .then() but no matter what, I can't make it wait for the results of client.detectPiiEntities block.
I'm sure it's something dumb. (Probably me.) Thanks in advance.

How to import a value in node js only after it is exported elsewhere sometime later?

I am learning SSO and trying this out without the conventional User class/object. I am new to asynchronous programming and having difficulty in managing the data flow. I am stuck at a point where I have successfully exported a boolean value, but my import (in another module) gets undefined. I suspect it is because import does not wait for the corresponding export statement to execute first. How do I make it and all subsequent code wait?
I don't know what to try in this case.
Module that is exporting usrFlag
const request = require("request");
let usrFlag = false; // assuming user doesn't already exist.
function workDB(usr_id, usr_name, dateTimeStamp) {
//some code excluded - preparing selector query on cloudant db
request(options, function (error, response, body) {
if (error) throw new Error(error);
if (body.docs.length == 0) addUsr(usr_id, usr_name, dateTimeStamp);
else {
xyz(true); //This user already exists in cloudant
console.log('User already exists since', body.docs[0].storageTime);
}
});
}
async function setUsrFlag(val) { usrFlag = val; }
async function xyz(val) {
await setUsrFlag(val);
//module.exports below does not execute until usrFlag has the correct value.
//so value is not exported until usrFlag has been properly set.
console.log(usrFlag);
module.exports.usrFlag = usrFlag;
}
Module that is importing this value
const usrP = require('../config/passport-setup');
const dbProcess = require('../dbOps/dbProcessLogic'); // <-- This is import
router.get('/google/redirect', passport.authenticate('google'), (req, res) => {
dbProcess.workDB(usrP.usrPrf.id, usrP.usrPrf.displayName, new Date());
// Instead of true/false, I see undefined here.
console.log(dbProcess.usrFlag);
});
I expect the require function of import module to wait for export module to send it all the required values. However, I know that is probably not going to happen without me explicitly telling it to do so. My question is, how?
So, I have just modified some of the code, so that I can work on it easily.
Module that is exporting usrFlag
// const request = require("request");
let usrFlag = false; // assuming user doesn't already exist.
function workDB(usr_id, usr_name, dateTimeStamp) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
xyz(true).then(function () {
resolve('done');
})
}, 1000);
});
}
function setUsrFlag(val) { usrFlag = val; }
function xyz(val) {
return new Promise(function (resolve, reject) {
setUsrFlag(val);
module.exports.usrFlag = usrFlag;
resolve('done');
});
}
module.exports = {
usrFlag,
workDB
}
Module that is importing this value
const dbProcess = require('../dbOps/dbProcessLogic'); // <-- This is import
dbProcess.workDB().then(function () {
console.log(dbProcess.usrFlag);
})
Now when you run the second file, you get usrFlag as true.
I have used setTimeout to imitate a request.
Sorry if I butchered up some of your code.

Sequential execution in node.js

I have code like
common.findOne('list', {'listId': parseInt(request.params. istId)}, function(err, result){
if(err) {
console.log(err);
}
else {
var tArr = new Array();
if(result.tasks) {
var tasks = result.tasks;
for(var i in tasks) {
console.log(tasks[i]);
common.findOne('tasks', {'taskId':parseInt(tasks[i])}, function(err,res){
tArr[i] = res;
console.log(res);
});
}
console.log(tArr);
}
return response.send(result);
}
});
It is not executed sequentially in node.js so I get an empty array at the end of execution. Problem is it will first execute console.log(tArr); and then execute
common.findOne('tasks',{'taskId':parseInt(tasks[i])},function(err,res){
tArr[i] = res;
console.log(res);
});
Is there any mistake in my code or any other way for doing this.
Thanks!
As you are probably aware, things run asynchronously in node.js. So when you need to get things to run in a certain order you need to make use of a control library or basically implement it yourself.
I highly suggest you take a look at async, as it will easily allow you to do something like this:
var async = require('async');
// ..
if(result.tasks) {
async.forEach(result.tasks, processEachTask, afterAllTasks);
function processEachTask(task, callback) {
console.log(task);
common.findOne('tasks', {'taskId':parseInt(task)}, function(err,res) {
tArr.push(res); // NOTE: Assuming order does not matter here
console.log(res);
callback(err);
});
}
function afterAllTasks(err) {
console.log(tArr);
}
}
The main things to see here is that processEachTask gets called with each task, in parallel, so the order is not guaranteed. To mark that the task has been processed, you will call callback in the anonymous function from findOne. This allows you to do more async work in processEachTask but still manage to signify when it is done. When every task is done, it will then call afterAllTasks.
Take a look at async to see all the helper functions that it provides, it is very useful!
I've recently created a simple abstraction named "wait.for" to call async functions in sync mode (based on Fibers): https://github.com/luciotato/waitfor
Using wait.for and async your code will be:
var wait = require('waitfor');
...
//execute in a fiber
function handleRequest(request,response){
try{
...
var result = wait.for(common.findOne,'list',{'listId': parseInt(request.params.istId)});
var tArr = new Array();
if(result.tasks) {
var tasks = result.tasks;
for(var i in tasks){
console.log(tasks[i]);
var res=wait.for(common.findOne,'tasks',{'taskId':parseInt(tasks[i])});
tArr[i] = res;
console.log(res);
}
console.log(tArr);
return response.send(result);
};
....
}
catch(err){
// handle errors
return response.end(err.message);
}
};
// express framework
app.get('/posts', function(req, res) {
// handle request in a Fiber, keep node spinning
wait.launchFiber(handleRequest,req,res);
});

Resources