Promise Chaining in Node.js - javascript

My promise-then chain does not appear to wait for each previous return statement.
new Promise(function (resolve, reject) {
console.log("1");
const http = require('http');
http.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
var info;
// process data
resolve(info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}).then(function (info) {
console.log("2");
if (info.item) {
console.log("item exists, don't retry");
return (info);
}
const http = require('http');
http.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
var info;
// process data
return(info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}).then(function (info) {
console.log("3");
const http = require('http');
http.get('otherurl' + info.item, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("processed");
return (info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}).then(function (info) {
console.log("4 " + info);
});
I want my output to be, eg.:
1
2
3
processed
4 [someinfo]
Here is what I get:
1
2
3
4 undefined
processed
It appears that only the first promise-then happen asynchronously. Why aren't the second and third then statements waiting for the prior return?

The code currently is:
new Promise(function (resolve, reject) {
console.log("1");
return(http.ClientRequest)
}).then(function (info) {
console.log("2");
return(http.ClientRequest)
}).then(function (info) {
console.log("3");
resolve(http.ClientRequest)
}).then(function (info) {
console.log("4 " + info);
});
To work a Promise chain needs to return a promise from the then part. But anything you return from then is treated as promise. But in your case
You are not returning anything.
If you are returning, it is from the callback, so basically it doesn't go out of the function. if you do return http.get(...) you will get http.ClientRequest object in your next then chain. Not the actual data.
So in your case a crude way to do this will be: Promisifying each http call.
new Promise(function (resolve, reject) {
console.log("1");
const http = require('http');
http.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
var info;
// process data
resolve(info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}).then(function (info) {
console.log("2");
if (info.item) {
console.log("item exists, don't retry");
return (info);
}
return new Promise(function (resolve, reject) {
const http = require('http');
http.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
var info;
// process data
resolve(info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
})
}).then(function (info) {
console.log("3");
return new Promise(function (resolve, reject) {
const http = require('http');
http.get('otherurl' + info.item, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("processed");
resolve(info);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
})
}).then(function (info) {
console.log("4 " + info);
});
Note: As I said it is a very non-elegant way of doing things, I would suggest using a promise based library like axios or maybe use async library instead of promises. Also you can use async-await. Each of them are better approach.

You are only resolving your first promise.
When you return a value inside a callback you are not resolving the promise.
You need to use the same strategy you use for the first one, wrapping the callback on a promise. So in your steps 2 and 3 you should return a new Promise and resolve it in the callback.
(...)
.then(function(result){
return new Promise(function(resolve, reject){
someThingWithCallback(result, function(err, data){
resolve(data)
})
})
})
.then(function(data){
...
})
You should try to avoid using a module that uses callbacks if you want to work with promises. You can use something like request-promise or axios.

Related

Undersanding request get in node.js

I'm trying to do a GET request in node.js. I would like to access the result of the request but there is a problem. Here is the code :
const https = require('https');
var data;
https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("Inside : "+data+"\n");
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
console.log("Outside : "+data+"\n");
The outside message appears before the inside one, and only the latter displays the request. That is due to asynchronous process I guess. But if I want to use my request outside, it's a problem because of this delay.
How may I do?
Your code execute asyncronymos. http.get(...) execute, but programm don't wait result of your get. So when you do "console.log("Outside : "+data+"\n");" - your data is empty. You will never get data to outside message, without js constructions like async/await.
This is what you need:
(async function () {
const https = require('https');
var data;
await new Promise((resolve, reject) => {https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("\n\n===>Inside : "+data+"\n");
resolve();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
reject();
});
});
console.log("\n\n===>Outside : "+data+"\n");
})();
Thanks for answering. After searches, I made this, which work, but I don't know if it's the best way to do this.
const https = require('https');
var data;
(async function () {
var p = new Promise((resolve) => {https.get('https://www.example.com', (resp) => {
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("\n\n===>Inside : "+data+"\n");
resolve();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
});
await p;
console.log("\n\n===>Outside : "+data+"\n");
})();

How can i return response only if one methodcall is completed in node js

I have a node js with express application. I need to expose a rest endpoint that will return the response of a http call. Whatever I try it returns before the http request. Can you please help
app.all('/query', function(req, res){
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http.get('http://test.com', (response) => {
let sdata = '';
response.on('data', (fragments) => {
sdata += fragments;
});
response.on('end', () => {
let response_body = sdata;
resolve(response_body.toString());
});
response.on('error', (error) => {
// promise rejected on error
reject(error);
});
});
});
urlCall.then((response) => {
var responseData=response;
res.json(responseData);
res.end();
}).catch((error) => {
console.log(error);
res.end();
});
}
Your code should work, but I suspect a request error not being handled (the error event handler being missing on your request)
You can try moving the error handler from the response to the request
app.all("/query", function (req, res) {
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http
.get("http://test.com", (response) => {
let sdata = "";
response.on("data", (fragments) => {
sdata += fragments;
});
response.on("end", () => {
let response_body = sdata;
resolve(response_body.toString());
});
})
.on("error", (error) => { // handling request errors
console.error("error");
// promise rejected on error
reject(error);
});
});
urlCall.then(
(response) => {
res.json(response);
res.end();
},
(error) => {
console.log(error);
res.end();
}
);
});

javascript promise callback

I am calling a javascript function , which in turn calls a web service;The response of this service is used to call another function which also calls a service. At end of both services we set session attributes. This code gives no errors, but the callback gets called before the service has returned data. The main motive of this code is to set the session attributes before return of flow from this code, when the callback gets called before the service has returned values the session attributes are not set and the requirement of the code is not fulfilled.
'use strict';
function close(sessionAttributes, fulfillmentState, message) {
return {
sessionAttributes,
dialogAction: {
type: 'Close',
fulfillmentState,
message : 'For security purpose answer these questions '
},
};
}
function getSecurityQuestions(intentRequest, context, post_options, callback){
const sessionAttributes = intentRequest.sessionAttributes || {};
var policynumber = sessionAttributes.policynumber;
var interactionID = sessionAttributes.interactionID;
var body = "";
var body2;
const http = require('https');
const promise = new Promise((resolve, reject) => {
const post_data = JSON.stringify({"Purpose":"SecurityQuestions", "InteractionID":interactionID, "SearchStringAcctNum":policynumber});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
context.done(body);
resolve(body);
});
res.on('error', function(e) {
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
callback( promise.then((body) => {
body2 = JSON.parse(body);
sessionAttributes.question1 = body2.SecurityDetails[0].Question;
close(sessionAttributes, 'Fulfilled');
}, (error) => {
console.log(error.message);
})
);
}
function getInteraction(intentRequest, context, callback) {
const slots = intentRequest.currentIntent.slots;
var policynumber = "PA"+slots.PolicyNumber;
var questionOne = slots.questionOne;
var questionTwo = slots.questionTwo;
const sessionAttributes = intentRequest.sessionAttributes || {};
console.log("policy number : "+policynumber + "question 1 : "+questionOne + "question 2 : "+questionTwo);
sessionAttributes.policynumber = policynumber;
var body = "";
var body2;
// An object of options to indicate where to post to
var post_options = {
host: 'example.com',
protocol: 'https:',
port: '3000',
path: '/hiddenPath',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const http = require('https');
const promise = new Promise((resolve, reject) => {
const post_data = JSON.stringify({"Purpose":"CreateInteraction"});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
context.done(body);
resolve(body);
});
res.on('error', function(e) {
console.log("rejected here");
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
callback( promise.then((body) => {
body2 = JSON.parse(body);
console.log("interaction ID : "+body2.InteractionID);
sessionAttributes.interactionID = body2.InteractionID;
getSecurityQuestions(intentRequest, context, post_options, callback);
}, (error) => {
console.log('Promise rejected.');
console.log(error.message);
}));
}
// --------------- Intents -----------------------
/**
* Called when the user specifies an intent for this skill.
*/
function dispatch(intentRequest, context, callback) {
const intentName = intentRequest.currentIntent.name;
if (intentName === 'currIntent') {
return getInteraction(intentRequest, context, callback);
}
throw new Error(`Intent with name ${intentName} not supported`);
}
// --------------- Main handler -----------------------
function loggingCallback(response, originalCallback) {
console.log("logging callback called......");
originalCallback(null, response);
}
exports.handler = (event, context, callback) => {
try {
dispatch(event, context, (response) => loggingCallback(response, callback));
} catch (err) {
callback(err);
}
};
You should resolve your promise only after the request ends.. Have updated your sample below. Hope it helps. Also, you were sending an invalid object as your post body. Fixed that as well.
function getValue(context, post_options, callback) {
var body = "";
var body2;
const http = require('http');
const promise = new Promise((resolve, reject) => {
// INVALID OBJECT
//const post_data = JSON.stringify({"something"});
const post_data = JSON.stringify({
something: "something"
});
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = http.request(post_options, function(res) {
res.on('data', function(chunk) {
body += chunk;
console.log("inside " + JSON.stringify(body));
// DONT RESOLVE HERE, REQUEST IS NOT COMPLETE
//resolve(body);
});
res.on('end', function() {
context.done(body);
//RESOLVE HERE INSTEAD
resolve(body);
});
res.on('error', function(e) {
reject(Error(e.message));
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
});
promise.then((body) => {
console.log("response data " + JSON.stringify(body));
body2 = JSON.parse(body);
callback(delegate(sessionAttributes, intentRequest.currentIntent.slots));
}, (error) => {
console.log('Promise rejected.');
console.log(error.message);
});
}

Getting files over HTTP in Node using promises

I wrote this function that fetches a file from a URL, taking redirects into account:
const fetchFile = (url, successCallback, errorCallback) => {
http.get(url).on('response', function(response) {
var body = ''
if((response.statusCode == 301) || (response.statusCode == 302)) {
console.log('Redirection for ' + url + ' to ' + response.headers.location + ' caught...')
fetchFile(response.headers.location, successCallback, errorCallback)
return
}
response.on('data', function(chunk) {
body += chunk
})
response.on('end', function() {
console.log('Completed!')
successCallback(body)
})
})
}
This code seems to work correctly, and now I am trying to rewrite it to use promises without any external modules, but I'm not making much headway. I came up with this code:
const fetchFileProm = function(url) {
console.log('In fetchFileProm()');
return new Promise((resolve, reject) => {
console.log('In Promise function body')
var req = http.get(url, (response) => {
console.log('In Response handler func body');
var body = ''
if((response.statusCode == 301) || (response.statusCode == 302)) {
console.log('Redirection for ' + url + ' to ' + response.headers.location + ' caught...')
return fetchFileProm(response.headers.location)
}
response.on('data', (fragment) => { body += fragment })
response.on('end', () => resolve(body) )
})
req.on('error', function(err) {
reject(err)
})
})
}
When I run this, I get the In Promise function body message but nothing further than that. What am I doing wrong here?
When you're in an asynchronous operation, a regular return doesn't work so when you're doing return fetchFileProm(response.headers.location), nothing takes care of that return and there's nothing that will ever resolve your promise.
What you need to do is to recursively pass the callbacks to the next asynchronous operation:
...
var req = http.get(url, (response) => {
console.log('In Response handler func body');
var body = ''
if((response.statusCode == 301) || (response.statusCode == 302)) {
console.log('Redirection for ' + url + ' to ' + response.headers.location + ' caught...')
fetchFileProm(response.headers.location).then(resolve, reject);
} else {
response.on('data', (fragment) => { body += fragment })
response.on('end', () => resolve(body) )
}
})
...

How to make synchronous http call using Promises in Nodejs

I would like to make http call synchronously using Q Promises, I have 100 students that I need for each of them to take some data from another platform and to do that I was trying via Q Promises but it does not seem like it is doing synchronously.
How do I make sure that another call is not being made once one is finished with parsing it's response and insertion into mongodb:
my code so far looks like this:
var startDate = new Date("February 20, 2016 00:00:00"); //Start from February
var from = new Date(startDate).getTime() / 1000;
startDate.setDate(startDate.getDate() + 30);
var to = new Date(startDate).getTime() / 1000;
iterateThruAllStudents(from, to);
function iterateThruAllStudents(from, to) {
Student.find({status: 'student'})
.populate('user')
.exec(function (err, students) {
if (err) {
throw err;
}
async.eachSeries(students, function iteratee(student, callback) {
if (student.worksnap.user != null) {
var worksnapOptions = {
hostname: 'worksnaps.com',
path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to,
headers: {
'Authorization': 'Basic xxxx='
},
method: 'GET'
};
promisedRequest(worksnapOptions)
.then(function (response) { //callback invoked on deferred.resolve
parser.parseString(response, function (err, results) {
var json_string = JSON.stringify(results.time_entries);
var timeEntries = JSON.parse(json_string);
_.forEach(timeEntries, function (timeEntry) {
_.forEach(timeEntry, function (item) {
saveTimeEntry(item);
});
});
});
callback();
}, function (newsError) { //callback invoked on deferred.reject
console.log(newsError);
});
}
});
function saveTimeEntry(item) {
Student.findOne({
'worksnap.user.user_id': item.user_id[0]
})
.populate('user')
.exec(function (err, student) {
if (err) {
throw err;
}
student.timeEntries.push(item);
student.save(function (err) {
if (err) {
console.log(err);
} else {
console.log('item inserted...');
}
});
});
}
function promisedRequest(requestOptions) {
//create a deferred object from Q
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var deferred = Q.defer();
var req = http.request(requestOptions, function (response) {
//set the response encoding to parse json string
response.setEncoding('utf8');
var responseData = '';
//append data to responseData variable on the 'data' event emission
response.on('data', function (data) {
responseData += data;
});
//listen to the 'end' event
response.on('end', function () {
//resolve the deferred object with the response
console.log('http call finished');
deferred.resolve(responseData);
});
});
//listen to the 'error' event
req.on('error', function (err) {
//if an error occurs reject the deferred
deferred.reject(err);
});
req.end();
//we are returning a promise object
//if we returned the deferred object
//deferred object reject and resolve could potentially be modified
//violating the expected behavior of this function
return deferred.promise;
}
Anyone could tell me what do I need to do to achieve such things?
Is it also possible so that I know when all of the http calls are finished and the insertion is done for all...
I would abandon your current approach and use the npm module request-promise.
https://www.npmjs.com/package/request-promise
It's very popular and mature.
rp('http://your/url1').then(function (response1) {
// response1 access here
return rp('http://your/url2')
}).then(function (response2) {
// response2 access here
return rp('http://your/url3')
}).then(function (response3) {
// response3 access here
}).catch(function (err) {
});
Now you just need to convert this to some kind of iteration for the 100 requests you want and the job will be done.

Categories

Resources