Get values outside npm request function - javascript

I'm trying to create a function that parses a json when it gets passed a URL. It parses it and if I try to log it inside the request it works, but I cannot return this value.
function JaySon(){
request({url: url,json: true}, function (error, response, body) {
return body;
})
}
var url ="http://api.geonames.org/findNearbyPlaceNameJSON?lat=51.9877644&lng=-1.47866&username=demo";
var x = JaySon(url);
console.log("json:"+x);
The console just logs "json:undefined", from my understanding it is because console.log gets ran faster than the parsing finishes. How could I go about fixing this issue?

This is due to the fact that you're not returning anything in your function :
async function JaySon(url) {
return new Promise((resolve, reject) => {
request({ url: url, json: true }, function(err, res, body) {
if (err) reject(err);
else resolve(body);
});
});
}
use it like :
Jayson('your_url').then(data => data)
or with async/await, but then it has to be wrap inside an async function

Another possible reason may also be due to the fact that your function doesn't take any arguments thus there's no request made to the URL you have passed when invoking the function.
Your code should be something like
function JaySon(url){
request({url: url,json: true}, function (error, response, body) {
return body;
})
}

Related

fs.readfile changes scope of global array and it can't be used outside it

I have 2 sections of code 1) that is called by 2nd to populate the array and write it into a file.
async function timeSeries(obj) {
data = [
{
original_value: []
}
]
//read file named as passed object's _id
await fs.readFile("./api/assignment_data/" + obj._id + ".json", "utf-8", function read(err, datas) {
if (err) {
throw err;
}
const filedata = JSON.parse(datas)
filedata.map(line => data[0].original_value.push(line.original_value))
})
setTimeout(() => {
try {
fs.writeFileSync("./api/timeseries.json", JSON.stringify(data), { encoding: 'utf8', flag: 'w' })
} catch (error) {
console.log(error)
}
}, 300);
}
The problem is, I can't access the global data array above after using it inside the fs.readfile function ( callback scope hell problem), I had to setTimeout then I am able to write it inside a file using another fs.writeFileSync function ( if I return the array I get a promise, I want data).How do I solve this? instead of writing it into another file and using it inside another route(below) how can I directly return the array in the second route and pass it as a json res?
section 2)
router.route("/api/debug/:num").get((req, res) => {
fs.readFile("./api/assignment_data/metrics.json", "utf8", function read(err, data) {
if (err) {
console.log(err);
}
const objdata = JSON.parse(data)
timeSeries(objdata[req.params.num])
})
fs.readFile("./api/timeseries.json", "utf8", function read(err, data) {
if (err) {
console.log(err);
}
const objdata = JSON.parse(data)
res.json(data)
})
})
If you use fs.readFile and want to do an action after the file has been read, you must do the action (write and read a file in your case) inside the callback function. Also, you can use fs.readFileSync if you can read synchronously.
First off, we need to explain a few things:
fs.readFile() is non-blocking and asynchronous. That means that when you call it, it starts the operation and then returns immediately and starts the execute the code that comes right after it. Then, some time later, it calls its callback.
So, your code is:
Calling fs.readFile()
Then, immediately setting a timer
Then, it's an indeterminate race between the fs.readFile() callback and the timer to see who finishes first. If the timer finishes first, then it will call its callback and you will attempt to access data BEFORE it has been filled in (because the fs.readFile() callback has not yet been called).
You cannot write reliable code this way as you are guessing on the timing of indeterminate, asynchronous operations. Instead, you have to use the asynchronous result from within the callback because that's the only place that you know the timing for when it finished and thus when it's valid. So, one way to solve your problem is by chaining the asynchronous operations so you put the second one INSIDE the callback of the first:
function timeSeries(obj, callback) {
//read file named as passed object's _id
fs.readFile("./api/assignment_data/" + obj._id + ".json", "utf-8", function read(err, datas) {
if (err) {
console.log(err);
// tell caller about our error
callback(err)
return;
} else {
let data = [{original_value: []}];
const filedata = JSON.parse(datas);
for (let line of filedata) {
data[0].original_value.push(line.original_value);
}
fs.writeFile("./api/timeseries.json", JSON.stringify(data), { encoding: 'utf8' }, (err) => {
if (err) {
console.log(err);
callback(err);
return;
} else {
// give our data to the caller
callback(data);
}
});
}
})
}
Then, to call this function, you pass it a callback and in the callback you can either see the error or get the data.
In modern nodejs, it's a bit easier to use async/await and the promise-based interfaces in the fs module:
const fsp = require('fs').promises;
async function timeSeries(obj) {
//read file named as passed object's _id
try {
let datas = await fsp.readFile("./api/assignment_data/" + obj._id + ".json", "utf-8");
const filedata = JSON.parse(datas);
let data = [{original_value: []}];
for (let line of filedata) {
data[0].original_value.push(line.original_value);
}
await fsp.writeFile("./api/timeseries.json", JSON.stringify(data), { encoding: 'utf8' });
return data;
} catch(e) {
console.log(e);
// handle error here or throw back to the caller
throw e;
}
}
For this version, the caller can use await and try/catch to get errors:
try {
let data = await timeSeries(obj);
// do something with data here
} catch(e) {
// handle error here
}
Based on what code you have written , I could just modify it using simple async-await - hope this helps
import fs from 'fs'
async function timeSeries(obj) {
const data = [{
original_value: []
}]
const assData = fs.readFileSync('./api/assignment_data/metrics.json', 'utf8')
const filedata = JSON.parse(assData)
filedata.map(line => data[0].original_value.push(line.original_value))
// no need for timeOut
fs.writeFileSync('./api/timeseries.json', JSON.stringify(data));
//return data if u need
return data
}
router.route("/api/debug/:num").get(async (req, res) => {
try {
const metricData = fs.readFileSync('./api/assignment_data/metrics.json', 'utf8')
const objdata = JSON.parse(data)
const timeSeriesData = await timeSeries(objdata[req.params.num])
// returning TimeSeriesData
res.status(200).json(timeSeriesData)
})
}
catch (error) {
res.status(500).send(error.message)
}

Get request data from a promise (RIOT API)

I have a request made with "request" from NodeJs, and I want to get the data with a promise. All good because the console shows me the information that I am requested. The problem is when I call the promise function, and the console shows me this. How can I get the real value?
let leagueTier = (accId) => new Promise(async (resolve, reject) => {
request({
url: `https://${reg}.api.riotgames.com/lol/league/v4/entries/by-summoner/${accId}${key}`,
json: true
}, (error, response, body) => {
if (!error && response.statusCode === 200) {
resolve(body[0].tier.toLowerCase())
} else {
reject(Error('It broke'))
}
})
}).then(function(res) {
return res;
})
This is where I call the function
.attachFiles([`./images/${leagueTier(body.id)}.png`])
(node:5868) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, stat 'D:\Dev\shen-bot\images[object Promise].png'
How can I access the string of the request I made? Maybe I shouldn't use promises?
The leagueTier return value will be a promise that eventually resolves or rejects into a value. You can't simply add the return value as part of a string. You can either use await or then to wait for the value to come through.
// asuming you are in async function context
.attachFiles([`./images/${await leagueTier(body.id)}.png`]);
// or
leagueTier(body.id).then(tier => {
.attachFiles([`./images/${tier}.png`]);
});
Note that your current code:
.then(function(res) {
return res;
})
Does absolutely nothing and can be omitted.

node.js wait for initialize variable before load other function

request('GET', url_ws).done((res) => {
if (res.statusCode==200) {
parseString(res.getBody(),{explicitArray:false}, function (err, result) {
pdfAnnotations=result['root']['element'];
console.log(pdfAnnotations);//show value//second
});
}
});
console.log(pdfAnnotations);//display "undefined"//first
fn_work(pdfAnnotations)
Hello, i have to work with variable loaded from web service, but when my function starts, variable is 'undefined'
You need to call your function after parseString() is done:
request('GET', url_ws).done(res => {
if (res.statusCode == 200) {
parseString(res.getBody(), { explicitArray: false }, function (err, result) {
const pdfAnnotations = result['root']['element']
doSomething(pdfAnnotations)
})
}
})
this is normal because the code is executed asynchronosly, it makes the request and then executes fn_work right after that, while fetching data from url_ws , then when it gets the data, it moves on to ParseString and so on,
the easy way is to move fn_work(pdfAnnontaions) inside the callback of the ParseString like so
request('GET', url_ws).done((res) => {
if (res.statusCode==200) {
parseString(res.getBody(),{explicitArray:false}, function (err, result) {
pdfAnnotations=result['root']['element'];
fn_work(pdfAnnotations);
});
}
});
i would recommend using promises or async/await , check these out :
https://blog.risingstack.com/mastering-async-await-in-nodejs/
https://www.valentinog.com/blog/http-requests-node-js-async-await/#Making_HTTP_requests_with_Nodejs_the_request_module

How to use request within async.each in node.js Express

In my node.js project, I need to make iterative API request so I'm using async.each method.
The console complains that url is not defined and I understand this because I've only declared urls which is an array of url.
I'm just not sure how I can put together request() from request module inside async.each. To satisfy async.each() I've placed urls as the first argument but request() itself requires a query string argument which is url.
I'm also using _.extend from Underscore to merge two responses but I'm not sure where I have it currently is in the right place.
Can someone please point me in the right direction?
var urls = [];
Object.keys(result.items).forEach(function(item) {
urls.push("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=" + result.items[item].contentDetails
.videoId + "&key=xxx");
})
async.each(
urls,
request(url, function(err, response, body) {
if (err) {
console.log(err);
return;
}
body = JSON.parse(body);
}),
function() {
var extended = _.extend(result, body);
getVideoDetailsCallback(null, extended)
});
It seems you're calling request with callbacks and all, and not just referencing it, which means you probably need an anonymous function call.
Also, if you want an array at the end, or whatever you're extending, you could just use async.map instead, and do something like
var urls = Object.keys(result.items).map(function(item) {
return "https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=" + result.items[item].contentDetails.videoId + "&key=xxx";
});
async.map(urls, function(url, callback) {
request(url, function(err, response, body) {
if (err) {
return callback(err);
}
callback(null, JSON.parse(body));
});
}, function(err, extended) {
if (err) {
// handle error
}
// extended is an array containing the parsed JSON
getVideoDetailsCallback(null, extended);
});
If you want to get the results from each iteration. I suggest using async.map. Map returns an array of results to the final callback.
Here's an example,
var async = require('async');
async.map([ 1, 2, 3, 4 ], function ( num, cb ) {
cb(null, num);
}, function (error, results) {
console.log(results);
});
// output: [ 1, 2, 3, 4 ]
As for your code snippet, your second argument is the wrong type. You're invoking request which doesn't return a function. The second argument is expected to be a function so what you need to do is wrap the request around function definition.
async.map(urls, function(url, callback) {
request(options, callback);
}, function (error, results) {
// do something with results
});

Async map not working

I have something like this
var async = require('async');
var request = require('request');//request urls
var urls = ['url1','url2', 'url3' ];
function requestUrl( url, callback){
console.log(callback.toString());
request(url, function(err, resp, body){
callback(null, body)
})
}
function cb(err, results){ console.log(results); }
then I call it via
async.map(urls, requestUrl, cb);
My 'cb' never gets called. I am printing out the 'callback' using .toString() method function in the second parameter and it looks like this.
function (err, v) {
results[index] = v;
callback(err);
}
Why is it ignoring my callback and putting it in its own?
async.map(arr, iterator, [callback])
arr - An array to iterate over.
iterator(item, callback) -The iterator is passed a callback(err, transformed) which must be called once it has completed with an error (which can be null) and a transformed item.
callback(err, results) - Optional A callback which is called when all iterator functions have finished, or an error occurs. Results is an array of the transformed items from the arr.
Results is an array.
Take a look at this runnable.
var async = require('async');
var request = require('request');
var urls = ['http://www.google.fr', 'http://twitter.fr'];
function requester(url, done) {
console.log('request: %s', url);
request(url, function(err, r, body) {
console.log('request is done');
console.log('have error on it ?', err !== null);
if (err) return done(err);
var res = res;
var b = body;
done(null, {
response: r,
body: b
});
})
}
async.map(urls, requester, function(err, r) {
console.log('async callback');
console.log('have error on it ?', err !== null);
if (err){
console.log('on error', err);
return;
}
console.log('it\'s a success! Now take a look at result :)');
console.log('results[%s]', r.length);
});
If you call your own endpoint (like http://localhost:3000/your/endpoint) make sure this request don't crash your node app.
This was my fault. When you request data and that request fails async tends to fail silently along with it not returning your request. If nothing seems to be returning from your requests I would say print out any and all errors and check if any conditions not being met.
For instance I had an
if
else if
which but neither was executing leaving it hanging and causing erratic behavior that was hard to pinpoint. 3 hour frustrating lesson in clean programming.

Categories

Resources