In NodeJs 8.* how do I apply Async / Await on http.get? - node.js

The following code gets the result asyncronously from the specified url, and I would like to return parsed variable out of getData method, after I receive the data, making use of async/await in nodejs version 8.* (without callback function).
function getData(v, r) {
var path = 'http://some.url.com';
var parsed = "";
http.get({
path: path
}, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
parsed = JSON.parse(body);
// now I would like to return parsed from this function without making use of callback functions, and make use of async/await;
});
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
return parsed;
};
Any help is greatly appriciated.

First off let me say I recommend using the npm package request to deal with http gets, that said.
1.) Use a Promise (await does this in the background)
function getData(v, r) {
var path = 'http://some.url.com';
var parsed = '';
return new Promise((resolve, reject) => {
http.get({
path: path
}, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
parsed = JSON.parse(body);
resolve(parsed);
});
}).on('error', function(e) {
reject(e.message);
});
});
};
Then usage would be
getData(v, r)
.then(success => console.log(success))
.catch(error => console.log(error))
2.) or callbacks You could pass in a callback as a parameter to getData (i.e. getData(v, r, callback)) then within the body of your function call it via callback(parsed) or callback(error_msg).
Then usage would be:
getData(v, r, result=>console.log(result))
or easier to read maybe:
function callback(res) {
console.log(res)
}
getData(v, r, callback)

Related

In NodeJS, how do I await the response of the http2 client library GET call?

I'm using the http2 client package with nodeJS. I want to execute a simple get request, and await the response from the server. So far I have
import * as http2 from "http2";
...
const clientSession = http2.connect(rootDomain);
...
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
});
process.exit(0);
But what I"m not able to figure out is how to do I await the response of the request before exiting? Right now the code flies through to the process.exit line and I can't see a way to block until the request is done.
If you want to await it, then you have to encapsulate it into a function that returns a promise and you can then use await on that promise. Here's one way to do that:
import * as http2 from "http2";
...
function getData(path) {
return new Promise((resolve, reject) => {
const clientSession = http2.connect(rootDomain);
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
resolve(data);
});
req.on('error', (err) => {
clientSession.destroy();
reject(err);
});
});
}
async function run() {
let data = await getData(path);
// do something with data here
}
run().then(() => {
process.exit(0);
}).catch(err => {
console.log(err);
process.exit(1);
});
The other way to do this is to use a higher level http library that does much of this work for you. Here's an example using the got module:
import got from 'got';
async function run() {
let data = await got(url, {http2: true});
// do something with data here
}
In this case, the got() module already supports http2 for you (if you specify that option), already collects the whole response for you and already supports promises (all the things your code needed to add in your original version).
Note, the GET method is the default method which is why it is not necessary to specify it here.
response = await new Promise(async (resolve, reject)=> {
let data = '';
req.on('data', async (chunk) => {
data += chunk;
resolve(JSON.parse(data));
});
});

Electron Method Callback

I'm new to electron and trying to build an application to control smarthome-components from my Mac. To do this, I need many HTTP-Request so the idea is to make an own method/function for this job.
Now my problem is, that I don't know how to use this callback-thing ;)
This is my code now:
const {app, Tray, Menu, BrowserWindow, net} = require('electron');
const path = require('path');
const iconPath = path.join(__dirname, 'icon.png');
let appIcon = null;
let win = null;
var http = require('http');
function httpGet(url, callback) {
http.get(url, (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
return callback(rawData);
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
}
app.on('ready', function(){
win = new BrowserWindow({show: false});
appIcon = new Tray(iconPath);
var contextMenu = Menu.buildFromTemplate([
{
label: 'http',
click: function() {
console.log(httpGet('http://192.168.178.10/switches/status_1'),
function(result) {
console.log(result);
}
);
}
}
]);
appIcon.setToolTip('This is my application.');
appIcon.setContextMenu(contextMenu);
});
Trey works, but the httpGet function does not return anything (undefined [function]) and electron crashes.
Would be really thankful if someone could help me with this.
Greetings,
Lukas
Bad news is that I do not know electron, so the TL;DR for the text below is as simple as: put a console.log line into the callback passed to http.get.
There is no return value in httpGet (and it is normal), that is why you can not log it. JavaScript event handling works with callbacks, and getting data via HTTP is an event.
What you have as second argument for http.get is the event handler.
While now it appears as a fancy lambda,
(res)=>{...}
in oldschool way it would look like
function(res){...}
So that is a function, and it will get invoked when something happens to the HTTP request you have issued, and that is where you could log/dig into the result (res).
What you see in your log at the moment:
undefined is the return value of a function which does not return anything, httpGet
[function] is the function you pass as second argument for console.log
function a(){} // a function which does not return anything
console.log(a(),a); // logs function result and function itself
First of all, the callback pattern is usually to write a function that accepts 2 parameters: err and result. If there was no error err becomes null, if there was an error result becomes null. So this would be the best way to write httpGet:
function httpGet(url, callback) {
http.get(url, (res) => {
const {
statusCode
} = res;
const contentType = res.headers['content-type'];
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => {
rawData += chunk;
});
res.on('end', () => {
return callback(null, rawData);
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
return callback(e, null)
});
}
Second, the way you wrote your call to httpGet is wrong. Change this:
console.log(httpGet('http://192.168.178.10/switches/status_1'),
function(result) {
console.log(result);
}
);
To
httpGet('http://192.168.178.10/switches/status_1', function(err, rawData) {
if (err) {
console.error('httpGet failed!');
} else {
console.log(rawData)
}
});

Write a stream to a string and return it from a function

As part of a program I am writing, I'd like to have a helper function that shortens some long URLs that are created. I found this package:
and am attempting to change the code so that it simply stores the string in a variable and returns it. I am having some issues with scope. the variable resData is never actually updated and return is always an empty string. How can I get this returned string into the global variable and return it? Thanks
var http = require("http")
module.exports = {
shorten: function(url) {
var resData = ''
http.get('[tinyurl api endpoint]' + encodeURIComponent(url), (res) => {
res.setEncoding('utf8')
res.on('data', (chunk) => {resData += chunk})
res.on('end', () => {
resData = resData.toString()
//this contains the link, and can be console.logged
})
}).on('error', (e) => {
console.log(e)
})
return resData //returns empty string
}
};
do this
var http = require("http")
module.exports = {
shorten: function(url,cb) {
var resData = ''
http.get('[tinyurl api endpoint]' + encodeURIComponent(url), (res) => {
res.setEncoding('utf8')
res.on('data', (chunk) => {resData += chunk})
res.on('end', () => {
resData = resData.toString()
//this contains the link, and can be console.logged
cb(null,resData) //<----- use callback (thanks robertklep)
})
}).on('error', (e) => {
console.log(e)
})
//--> return resData //returns empty string as node is non-blocking io, this line will be executed before http response is received
}
};
//index.js
var http = require('./path/to/that/module')
http.shorten(url,function(error,result){ console.log(result) })
Try to use with callback function
shorten: function(url,callback) {
var resData = ''
http.get('[tinyurl api endpoint]' + encodeURIComponent(url), (res) => {
res.setEncoding('utf8')
res.on('data', (chunk) => {resData += chunk})
res.on('end', () => {
resData = resData.toString()
//this contains the link, and can be console.logged
callback(null,resData); //<----here
})
}).on('error', (e) => {
console.error(e);
callback(e,null); //<----- and here
})
}

Using Q library for HTTP api response testing in nodejs

how to use Q to make it wait until previous response has come from the server.
What I am looking to do here is compare the response from test server and production server for the same request.
I get the responses back from both the servers, but unable to compare them since the assert statement is executed before the response comes back.
Any one know what I am doing wrong. heres the code.
var Q = require('q');
var path='';
var prodResponse = '';
var tstReponse = '';
Q.fcall(readFile())
.then(secondFunction())
.then(thirdFunction())
.then(function(){
console.log("prodResponse: "+prodResponse);
console.log("tstResponse: "+tstResponse);
assert.strictEqual(prodResponse, tstResponse)
})
.catch(function(){
console.log('error occurred');
})
.done();
function readFile(){
fs.readFile('hostname.json', function (err, data) {
if (err) return console.error(err);
path = JSON.parse(data);
return JSON.parse(data);
});
}
function secondFunction(){
var prodOptions = {
hostname: 'somehostname.com',
port: 80,
path: "/path?"+path.path,
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
auth : ''
};
return http.request(prodOptions, function(res) {
console.log('Prod');
res.setEncoding('utf8');
res.on('data', function (chunk) {
prodResponse = chunk;
return chunk;
});
res.on('end', function() {
console.log('No more data in response.');
})
}).on('error', function(e) {
console.log('problem with request: ' + e.message);
}).end();
}
function thirdFunction(){
// same a second, only difference is the response http.
}
There is multiple errors in your code
Q.fcall(readFile())
Your q variable is q and not Q. So this line will crash because Q is undefined (javascript is case sensitive).
Then, readFile doesn't return any promise (in fact, it returns nothing). So the q library can't use anything to wait the end of any asynchronous work. The then callbacks will be fired immediatly.
You can use Q.ninvoke to make your readFile function return a promise, and you can use Q.defer to create and return a promise from your secondFunction:
var Q = require('q');
var path='';
var prodResponse = [];
var tstReponse = '';
readFile()
.then(secondFunction())
.then(thirdFunction())
.then(function(){
console.log("prodResponse: "+prodResponse);
console.log("tstResponse: "+tstResponse);
assert.strictEqual(prodResponse, tstResponse)
})
.catch(function(){
console.log('error occurred');
})
.done();
function readFile(){
return Q.ninvoke(fs, 'readFile', 'hostname.json').then(function (data) {
path = JSON.parse(data);
return path;
}, function (err) {
console.error(err);
});
}
function secondFunction(){
var prodOptions = {
hostname: 'somehostname.com',
port: 80,
path: "/path?"+path.path,
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
auth : ''
};
var defer = Q.defer();
var chunks = [];
http.request(prodOptions, function(res) {
console.log('Prod');
res.setEncoding('utf8');
res.on('data', function (chunk) {
chunks.push(chunk);
});
res.on('end', function() {
console.log('No more data in response.');
prodResponse = chunks.join('');
defer.resolve(prodResponse);
})
}).on('error', function(e) {
console.log('problem with request: ' + e.message);
defer.reject(e);
}).end();
return defer.promise;
}
function thirdFunction(){
// same a second, only difference is the response http.
}

How can you synchronize this process using nodejs?

I need to iterate on an array, for each item I apply an operation by calling an HTTP call.
The difficulty is that i need to syncronize this process in order to call a callback after the loop (containing the array after all the operation executed by the HTTP call).
Let's consider this short example:
function customName(name, callback) {
var option = {
host:'changename.com',
path: '/'+name,
headers: { 'Content-Type': 'application/json' },
port: 80,
method:'POST'
};
var req = http.request(option, function(res) {
var output = "";
res.on('data', function (chunk) {
output += chunk;
});
res.on('end', function() {
var obj = JSON.parse(output);
callback(obj.res);
});
});
req.on('error', function(e) {
console.error(e.message);
});
req.end();
}
function changeNames(OldNames, callback) {
var Res = [];
for (name in OldNames) {
customName(OldNames[name], function(new_name) { Res.push(new_name); });
});
callback(Res);
}
var Names = ['toto', 'tata', 'titi'];
changeNames(Names, function(Names) {
//...
});
Here the loop is over before the first HTTP call, so the Res array is empty.
How can we synchronize this execution?
I know it's not very good to synchronize treatments in nodejs. Do you think it would be better to communicate the names one by one with the client and not building an array?
You can use async.map for that. You pass it your list of names, it will run the getOriginalName function (which you mistakenly called customName, I think) for each name and gather the result, and in the end it will call a function with an array of results:
var http = require('http');
var async = require('async');
function getOriginalName(name, callback) {
var option = {
host:'changename.com',
path: '/'+name,
headers: { 'Content-Type': 'application/json' },
port: 80,
method:'POST'
};
var req = http.request(option, function(res) {
var output = "";
res.on('data', function (chunk) {
output += chunk;
});
res.on('end', function() {
var obj = JSON.parse(output);
callback(null, obj.res);
});
});
req.on('error', function(e) {
callback(e);
});
req.end();
}
function changeNames(OldNames, callback) {
async.map(OldNames, getOriginalName, callback);
}
var Names = ['toto', 'tata', 'titi'];
changeNames(Names, function(err, NewNames) {
console.log('N', NewNames);
});

Resources