NodeJS - Nested async functions - node.js

in an express router .post which is async, I have this line:
var recaptcha = await tokenValidate(req);
tokenValidate is below:
async function tokenValidate(req) {
// some code to generate a URL with a private key, public key, etc.
return await tokenrequest(url);
}
Then tokenrequest is below: (note request is the npm request library)
async function tokenrequest(url) {
request(url, function(err, response, body){
//the body is the data that contains success message
body = JSON.parse(body);
//check if the validation failed
if(body.success !== undefined && !body.success){
return false;
}
//if passed response success message to client
return true;
})
}
Issue is the nested async functions. The initial recaptcha variable returns undefined since 'request' doesn't resolve, even though I'm using await. How can I get recaptcha to wait for tokenValidate which in turn has to wait for tokenrequest?
Thank you.

A couple issues:
Your tokenrequest function is not returning anything (the return value in the request callback function won't be returned)
await tokenrequest(url); won't work as you expect because the request library is using a callback (not async/await)
This can be fixed by returning a Promise:
async function tokenrequest(url) {
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
//the body is the data that contains success message
body = JSON.parse(body);
//check if the validation failed
if (body.success !== undefined && !body.success) {
resolve(false);
} else {
//if passed response success message to client
resolve(true);
}
});
});
}

Your tokenRequest() function is returning a promise (because it's async), but that promise resolves immediately with no value that is attached to your call to request(). And, the return values you do have are just returning to the request() callback where they are ignored, not from your actual top level function.
What I would suggest is that you ditch the request() module because it's deprecated and does not support promises and pick a new more modern module that does support promises. I use the got() module which would make this whole thing a LOT easier and a lot fewer lines of code.
const got = require('got');
async function tokenrequest(url) {
let result = await got(url).json();
return !!result.success;
}

Related

Node Js loop in Promise All not waiting to get request from SOAP

Hi I try to use for for loop data and send request to soap api. The problem is my program seems like it didn't wait to get request from soap request
here is my example data
data = [
{
store: "store1",
product: "product2",
},
{
store: "store2",
product: "product3",
}
]
exports.thisIsFunc = async (req, res, next) => {
Promise.all(data).then(result => {
for (let item of result) {
i++
console.log(i)
args.CustomerCode = item.store
args.ItemNo = item.product
const getApi = apiSend()
}
});
}
export function apiSend() {
return new Promise ((resolve, reject) => {
soap.createClient(url, (err, client) => {
client.getDataFromAPI(args, (err, result) => {
console.log(result)
return result
})
});
});
}
as you see I try to use new Promise in sendApi function but sometimes It stop the error show up
TypeError: Cannot read property 'getDataFromAPI' of undefined
sometimes it return response from api. The reason that I didn't use async,await in soap because I try to change soap function into async function but it didn't work.
apiSend() has the following issues:
You ignore both callback errors.
You never resolve() or reject() the promise you return.
You can fix it like this:
export function apiSend() {
return new Promise ((resolve, reject) => {
soap.createClient(url, (err, client) => {
if (err) return reject(err);
client.getDataFromAPI(args, (err, result) => {
if (err) return reject(err);
console.log(result)
resolve(result);
})
});
});
}
Then, in thisIsFunc() you have a number of issues:
There's no point in using Promise.all() on a static array of values. It only does something useful if you pass it an array with at least one promise in it.
There's no declaration of i or args
You don't do anything with the promise returns from apiSend()
It's not really clear what you're trying to do here, but if you want thisIsFunc() to return a promise that resolves with an array of results from all the calls to apiSend(), that could be structured like this:
exports.thisIsFunc = () => {
return Promise.all(data.map(item => {
let args = {
CustomerCode: item.store,
ItemNo: item.product
};
return apiSend(args);
}));
}
This implementation uses data.map() to iterate the array and create an array of promise from calling apiSend(). It then uses Promise.all() to collect all the results from the array of promises into an array of results that is the resolved value of the single returned promise.
It appears you were attempting to declare thisIsFunc() as an Express request handler. You can do that, but then you will need to complete the request inside that function by sending a response for both success and error conditions. As I've shown it above, this is a helper function that retrieves a set of data and then returns a promise that resolves to an array of results. You can use that in a request handler as needed or you can add more code to this to make it into a request handler that sends a response to the incoming request and returns errors responses appropriately.

Asynchronous await In Node.js

How to use the asynchronous Await in Node.js by using these function and how
The request.get() function returns a Promise by which user will await...
I have tried the Below code so far and also gave the explanation below
async function fun1(req, res){
let response = await request.get('http://localhost:3000');
if (response.err) { console.log('error');}
else { console.log('fetched response');
}
The code above basically asks the javascript engine running the code to wait for the request.get() function to complete before moving on to the next line to execute it. The request.get() function returns a Promise for which user will await . Before async/await, if it needs to be made sure that the functions are running in the desired sequence, that is one after the another, chain them one after the another or register callbacks.
async function fun1(req, res){
let response = await request.get('http://localhost:3000');
if (response.err) { console.log('error');}
else { console.log('fetched response');
}
request package does not use return promise. Use the request-promise package which wraps the request with Promise.
You can use it like:
const rp = require('request-promise')
async function getSomeData() {
try {
const url = 'http://some.com'
// waits for promise to resolve
const data = await rp(url)
// data contains resolved value if successfull
// continue some other stuff
...
} catch (e) {
// handle error if error occurred
console.error(e)
}
}

Dialogflow v1 webhook - speech not returning immediately after an async call

I am using DialogFlow V1 node.js webhook and I have a problem.
I have an intent that will connect to the Spotify API to get tracks for my chatbot:
const {DialogflowApp} = require('actions-on-google');
function spotifyIntent(app) {
//Get search parameters
[...]
return asyncFunction(query).then(function (message) {
this.app.ask(message);
return Promise.resolve();
}).catch(function (err) {
this.app.ask("Une erreur est apparue: "+err);
return Promise.resolve();
})
}
with my async function being:
function asyncFunction(query) {
spotifyApi.clientCredentialsGrant()
.then(function(data) {
// spotifyApi.setAccessToken(data.body['access_token']);
return spotifyApi.searchTracks(query);
}).then(function(data) {
const link = data.body.tracks.items[0].preview_url;
let speech = '<speak>Here is the first result found.<audio src="'+ link +'">I didn\'t found it</audio></speak>';
return Promise.resolve(speech);
}).catch(function(err) {
return Promise.reject(err);
});
}
A Promise is hidden in the call of clientCredentialsGrant() and searchTracks();
My intent is called by the classic map with action: intentFunction.
I read here that the ask method should work but it doesn't for me. I tried the version with the Promise and with a callback but when I simulate my code I always get the answer:
"message": "Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response
I don't understand what I'm doing wrong here? I know the problem come from the request being async but it should work fine with the Promise or the callback because right now it returns instantly?
It returns instantly because, although asyncFunction() calls something that returns a Promise, and although the then() and catch() portions return a Promise... asyncFunction() itself does not return a promise. In fact, it doesn't return anything explicitly, so it returns undefined.
You probably want that code to be
function asyncFunction(query) {
return spotifyApi.clientCredentialsGrant()
.then(function(data) {
// spotifyApi.setAccessToken(data.body['access_token']);
return spotifyApi.searchTracks(query);
}).then(function(data) {
const link = data.body.tracks.items[0].preview_url;
let speech = '<speak>Here is the first result found.<audio src="'+ link +'">I didn\'t found it</audio></speak>';
return Promise.resolve(speech);
}).catch(function(err) {
return Promise.reject(err);
});
}
Note the change in the first line that adds the return statement.
This is a common pattern (and a frequently overlooked problem) when dealing with async functions that need to return a Promise.

How come async/await doesn't work in my code?

How come this async/await doesn't work?
I've spent all day trying different combinations, watching videos and reading about async/await to find why this doesn't work before posting this here.
I'm trying to make a second nodejs app that will run on a different port, and my main app will call this so it scrap some data and save it to the db for cache.
What it's suppose to do:
Take a keyword and send it to a method called scrapSearch, this method create a complete URI link and send it to the method that actually get the webpage and returns it up to the first caller.
What is happening:
The console.log below the initial call is triggered before the results are returned.
Console output
Requesting : https://www.google.ca/?q=mykeyword
TypeError: Cannot read property 'substr' of undefined
at /DarkHawk/srv/NodesProjects/_scraper/node_scrapper.js:34:18
at <anonymous>
app.js:
'use strict';
var koa = require('koa');
var fs = require('fs');
var app = new koa();
var Router = require('koa-router');
var router = new Router();
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3002, 'localhost');
router.get('/scraptest', async function(ctx, next) {
var sfn = require('./scrap-functions.js');
var scrapFunctions = new sfn();
var html = await scrapFunctions.scrapSearch("mykeyword");
console.log(html.substr(0, 20));
//Normally here I'll be calling my other method to extract content
let json_extracted = scrapFunctions.exGg('mykeywords', html);
//Save to db
});
scrap-functions.js:
'use strict';
var request = require('request');
var cheerio = require('cheerio');
function Scraper() {
this.html = ''; //I tried saving html in here but the main script seems to have issues
retrieving that
this.kw = {};
this.tr = {};
}
// Search G0000000gle
Scraper.prototype.scrapSearch = async function(keyword) {
let url = "https://www.google.ca/?q="+keyword";
let html = await this.urlRequest(url);
return html;
};
// Get a url'S content
Scraper.prototype.urlRequest = async function(url) {
console.log("Requesting : "+url);
await request(url, await function(error, response, html) {
if(error) console.error(error);
return response;
});
};
module.exports = Scraper;
I tried a lot of things but I finally gave up - I tried putting await/async before each methods - didn't work either.
Why that isn't working?
Edit: wrong function name based on the fact that I created 2 different projects for testing and I mixed the file while copy/pasting.
You are not returning anything from urlRequest. Because it is an async function, it will still create a promise, but it will resolve with undefined. Therefore your html is undefined as seen in the error.
The problematic part is the request function which is a callback style function, but you're treating it as a promise. Using await on any value that is not a promise, won't do anything (technically it creates a promise that resolves directly with the value, but the resulting value remains the same). Both awaits within the urlRequest are unnecessary.
request(url, function(error, response, html) {
if(error) console.error(error);
// This return is for the callback function, not the outer function
return response;
});
You cannot return a value from within the callback. As it's asynchronous, your function will already have finished by the time the callback is called. With the callback style you would do the work inside the callback.
But you can turn it into a promise. You have to create a new promise and return it from urlRequest. Inside the promise you do the asynchronous work (request) and either resolve with the value (the response) or reject with the error.
Scraper.prototype.urlRequest = function(url) {
console.log("Requesting : "+url);
return new Promise((resolve, reject) => {
request(url, (err, response) => {
if (err) {
return reject(err);
}
resolve(response);
});
});
};
When an error occurred you want to return from the callback, so the rest (successful part) is not executed. I also removed the async keyword, because it's manually creating a promise.
If you're using Node 8, you can promisify the request function with the built-in util.promisify.
const util = require('util');
const request = require('request');
const requestPromise = util.promisify(request);
Scraper.prototype.urlRequest = function(url) {
console.log("Requesting : " + url);
return requestPromise(url);
};
Both versions will resolve with the response and to get the HTML you need to use response.body.
Scraper.prototype.scrapSearch = async function(keyword) {
let url = "https://www.google.ca/?q=" + keyword;
let response = await this.urlRequest(url);
return response.body;
};
You still need to handle errors from the promise, either with .catch() on the promise, or using try/catch when you await it.
It is absolutely essential to understand promises when using async/await, because it's syntactic sugar on top of promises, to make it look more like synchronous code.
See also:
Understand promises before you start using async/await
Async functions - making promises friendly
Exploring ES6 - Promises for asynchronous programming

Promise is undefined in procedural code in nodejs

I'm very new to async languages like nodejs, I'm trying to write a web scraper that will visit a link, download a code, extract with regex, THEN visit another link using that code. I'm aware I could use callbacks, but I expect to have to go 8-9 levels deep, I think promises is the way to go (is there a better way?)
var promise = require("promise");
var request = require("request");
login();
function get_login_code()
{
request.get("someurl.com", function (error, response, body)
{
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
return login_code
});
}
function login()
{
var login_promise = promise.resolve(get_login_code());
console.log(login_promise);
}
I've tried a bunch of combinations of messing around with promises, but I either always get undefined or a promise which doesn't have a value. I don't want to nest promise functions inside promises because that's exactly the same thing as callback hell. Can someone tell me what I'm doing wrong, I really want this code to be procedural and not 8 callbacks. In the ideal world promise.resolve just waits until get_login_code() returns the actual code, not undefined.
Output:
Promise { _45: 0, _81: 1, _65: undefined, _54: null }
Desired Output:
hello world
What your code do:
calls get_login_code that returns nothing (i.e. undefined)
inside of login function you create a new promise that is immediately resolved to the result of get_login_code, i.e. undefined.
Thus, you do not use login_code at all.
To make it work, you should make get_login_code to return a promise that will be resolved to login_code. Consider you use promise npm module, the code may look like:
// uppercased, it's a constructor
var Promise = require("promise");
var request = require("request");
login();
function get_login_code()
{
return new Promise(function (resolve, reject) {
request.get("someurl.com", function (error, response, body) {
if (err) {
reject(err);
return;
}
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
resolve(login_code);
});
});
}
function login()
{
// return a new promise to use in subsequent operations
return get_login_code()
.then(function(login_code) {
console.log(login_code);
});
}
You should create new promise in the function to handle reject and resolve not by handling resolve to the function itself. Use then to get the response value from promise. I guess this should work.
var promise = require("promise");
var request = require("request");
function get_login_code()
{
var promise = new Promise(function(resolve, reject) {
request.get("someurl.com", function (error, response, body)
{
if (error) {
reject(error);
} else {
// just for example
body = 'TOKEN" value="hello world"';
var login_code = body.match(/.TOKEN" value="([^"]+)../i);
resolve(login_code);
}
});
});
}
get_login_code()
.then(function (code) {
console.log(code);
});

Resources