How come async/await doesn't work in my code? - node.js

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

Related

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);
});

NodeJS - Nested async functions

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;
}

Why is it not going inside the function? It only output "finish" and "start" on AWS Lambda

I'm doing a html to pdf function using the phantom-html-to-pdf on AWS Lambda with NodeJS. But I encountered a problem that did not go inside the function. The output only shows Start, Finish and Done is not shown which means it's not going in the function. What is the issue here?
var fs = require('fs')
var conversion = require("phantom-html-to-pdf")();
exports.handler = async (event) => {
console.log("Start")
conversion({ html: "<h1>Hello World</h1>" },
async(err, pdf) => {
var output = fs.createWriteStream('output.pdf')
console.log(pdf.logs);
console.log(pdf.numberOfPages);
// since pdf.stream is a node.js stream you can use it
// to save the pdf to a file (like in this example) or to
// respond an http request.
pdf.stream.pipe(output);
console.log("Done")
});
console.log("Finish")
};
The problem is you have marked your lambda function as async which means your function should return a promise. In your case you are not returning a promise. So you have two choices here
Convert the conversion function from callback to promise based.
OR instead of marking the function async, add callback and execute that. something like this
const fs = require("fs");
const conversion = require("phantom-html-to-pdf")();
exports.handler = (event, context, callback) => {
console.log("Start");
conversion({"html": "<h1>Hello World</h1>"},
// eslint-disable-next-line handle-callback-err
async (err, pdf) => {
const output = fs.createWriteStream("output.pdf");
console.log(pdf.logs);
console.log(pdf.numberOfPages);
// since pdf.stream is a node.js stream you can use it
// to save the pdf to a file (like in this example) or to
// respond an http request.
pdf.stream.pipe(output);
console.log("Done");
callback(null, "done");
});
};

run mongoose methods synchronously in node.js

I'm trying to call getMarchandiseList() method that call a mongoose method " find({}) " in my marchandise router.
the problem is when I send the responce I find that I get a result=0 before the find({}) method send it's result
//the result should be
{id_marchandise : 01 , libelle_marchandise : 'fer'}.
[MarchandiseDAO.js][1]
*
var express = require("express");
var router = express.Router();
var marchandiseDAO = require ('../DAO/marchandises');
router.get('/listmarchandise', function (req,res) {
var marchandise = new marchandiseDAO();
marchandise.getMarchandiseList();
res.send("result" +marchandise.result);
});
module.exports = router;
*
[marchandiserouter.js][2]
var model = require("../Models/model");
var marchandiseDAO = function () {
console.log("get instance ");
this.result = 0 ;
}
marchandiseDAO.prototype.getMarchandiseList = function () {
console.log("begin");
model.MarchandiseModel.find({},function(err,marchandiselist){
console.log("traitement");
if(err) result = err;
else result = marchandiselist;
});
console.log("end");
}
You cannot run mongoose methods synchronously. But if you use a modern version of Node then you can make it appear as if it was run asynchronously.
Unfortunately you posted your code examples as screenshots which would require me to use Photoshop to fix your code, which is obviously not worth the hassle. So instead I will show you a general solution.
Using normal promises:
Model.find({...}).then(data => {
// you can only use data here
}).catch(err => {
// always handle errors
});
Using await:
try {
let data = await Model.find({...});
// you can only use data here
} catch (err) {
// always handle errors
}
The second example can only be used in an async function. See this for more info:
try/catch blocks with async/await
Use await outside async
Using acyns/await in Node 6 with Babel
When do async methods throw and how do you catch them?
using promises in node.js to create and compare two arrays
Keeping Promise Chains Readable
function will return null from javascript post/get

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