JavaScript Handle Asynchronous Callbacks node .js - javascript

I am new to Javascript and just got stuck with async callbacks with Javascript using Node.js.
I first set up the Facebook webhook and make a Webhook POST request
Here is my code :
routes.js
**To set up facebook webhook**
var facebook_handler = require('../controllers/botkit').handler
module.exports = function (app) {
// public pages=============================================
// root
app.get('/', function (req, res) {
res.render('home')
})
app.get('/webhook', function (req, res) {
// Check to see which webhook password (FACEBOOK_VERIFY_TOKEN) to check for, from incoming request.
if (process.env.PORT ||process.env.VCAP_APP_PORT ) {
FB_VERIFY_TOKEN = process.env.FACEBOOK_VERIFY_TOKEN
} else {
FB_VERIFY_TOKEN = process.env.FACEBOOK_VERIFY_TOKEN_DEV
}
// This enables subscription to the webhooks
if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === FB_VERIFY_TOKEN) {
res.send(req.query['hub.challenge'])
}
else {
res.send('Incorrect verify token')
}
})
app.post('/webhook', function (req, res) {
console.log("\n CALL HANDLER FUNCTION ---- \n");
facebook_handler(req.body)
console.log("call handler done");
res.send('okay')
})
}
From Above code, i make a POST request to Facebook webhook and get the details of the FB message and then process the webhook POST request in another file BotKit.js
Botkit.js
var request = require('request');
require('dotenv').load();
var handler = function (obj) {
console.log("Message received from FB \n");
if (obj.entry ) {
for (var e = 0; e < obj.entry.length; e++) {
for (var m = 0; m < obj.entry[e].messaging.length; m++) {
var facebook_message = obj.entry[e].messaging[m]
test_message = facebook_message.message.text;
translatorEnglish (test_message) // calling the watson translator api to get translation for the received facebook message.
}
}
}
Above code process webhook POST request and call the Translator function ( translation POST request )
Translator Function
var translationusername = "1234"
var translationpassowrd = "1234"
var transURL = "https://gateway.watsonplatform.net/language-
translator/api/v2/translate";
translatorEnglish = function(test_message) {
console.log("this should be called when translator called:" +test_message);
var parameters = {
text: test_message,
model_id: 'es-en'
};
languageTranslator.translate(
parameters,
function(error, response, body) {
if (error)
console.log(error)
else
english_message = response.translations[0].translation
console.log("The response should be:" +english_message);
translate = false
//console.log(JSON.stringify(response, null, 2));
}
);
};
The problem is the translation POST request is not executed until the Call Handler i.e the webhook POST request is completed. The translation POST request always executes after Webhook POST is completed.
Is there are way i can execute the Translator POST request within the Webhook POST request before the Webhook POST request is complete.
Something like this
Webhook POST --> execute --> Translation POST execute and complete ---> Webhook POST complete

First of all,languageTranslator.translate function is an async function so it returns and doesn't waits for it's callback to be done. So if you want to be sure that the callback finished, you should do the next commands after it's finished by utilizing new callbacks or promise.then function.
Secondly, your translatorEnglish function neither have callback, nor is a promise. Hence, nobody waits for its async function call (languageTranslator.translate) to be finished. Thus, You should change it to either a promise format or callback format (also you can use async await format which is like promise).
Thirdly, anyway translatorEnglish function will be an async function and you want to use it inside a for loop in handle function which means you may have more than one async function to wait for and it makes it hard to be handled using callback. So I suggest you to use promise in this case and in order to wait for all promises to be completed you can use
Promise.all function.
Don't forget, you should make all those functions promise and call res.send in facebook_handler().then function.
extras:
If you want to convert a function with callback to promise without changing its implementation (promisify) you can use node 8 util.promisify or bluebird Promise.promisify functions.
Take a look at this question and answer too.

Related

NodeJS: Wait for Status Code of Post Request

Background:
I have a gateway that returns the status code 200 via a post request if it is completely booted. If not it returns 500 while booting.
My idea:
I have a NodeJS application which should wait until the gateway returns 200. So I created a while loop which checks the state of the gateway.
My problem:
Unfortunately nothing works, the state is always true. Non of the log statements in the request will be display.
Do you have tips for me how I can fix this?
while (isGatewayUnavailable()) {
log.info('waiting for gateway ...');
sleep(60)
}
function isGatwayUnavailable() {
const url = '...'
let state = true
request.post(url, (err, res, body) => {
log.debug(0)
if (err) {
log.debug("Gateway offline");
log.debug("err: " + err);
}
else if (res.statusCode === 200) {
log.debug("Gateway online");
state = false;
cb(true);
}
else {
log.debug("Status Code: " + res.statusCode);
}
});
log.debug('return state: ' + state);
return state;
}
There is no "waiting" in JS. There's only "running code" and "running code in response to signals" (events, callbacks, promises). In this case, you want to do something based on a process that you do not control the timing of, so you can't use a synchronous function: by the time the function reaches its return keyword, you don't have any information to return yet..
So, instead of making your function return a value and having the caller wait for that value, make your code "do things once the information is in". That is, make your function either generate an event that you have a handler registered for, or pass a callback as argument so that your function can run that callback once it has the information necessary, or have it return a promise whose resolve (or reject) gets called once you have the information necessary.
1. Event-based:
const pubsub = ...;
function checkGatwayAvailability() {
request.post(url, (err, res, body) => {
pubsub.signal("gateway:availability", { available: ..., error: ... });
});
}
with caller code:
const pubsub = ...;
pubsub.register("gateway:availability", data => {...});
...
checkGatewayAvailability();
In this, the code that calls this and the code that handles the result are 100% detached from each other. Also note that pubsub isn't a real thing. Depending on your framework and APIs, there will be different ways to achieve event generation/handling, or you might even need to write your own (which really means "hit up npm and find one that is well documented and used by many folks, then use that").
2. Using a callback:
function checkGatwayAvailability(reportResult) {
request.post(url, (err, res, body) => {
reportResult({ available: ..., error: ... });
});
}
with caller code:
checkGatwayAvailability( result => {
...
});
In this approach, the calling and handling code are coupled in the sense that your call points to the handler, even if your handler is declared somewhere completely different, like:
checkGatwayAvailability(NetworkMonitor.handleGatewayResponse);
3. Using a promise:
function checkGatwayAvailability(reportResult) {
return new Promise((resolve, reject) => {
request.post(url, (err, res, body) => {
if (err) reject(err);
resolve(...);
});
});
}
with caller code:
checkGatwayAvailability().then(result => {...}).catch(err => {...});
Similar to a callback, the calling and handling code are coupled, but with promises you don't "guess" at whether the resultant information is good or bad, you literally have separate code paths for the "good" cases (handled by then), and the "bad" cases (handled by catch).
3b. Using a promise through async/await syntax:
In this case, request.post does not return a Promise, your function would still need to bake its own promise, so using an async declaration doesn't make a lot of sense. We can still use the await keyword in the calling code, though:
try {
const result = await checkGatwayAvailability();
} catch (e) {
...
}
but only if that caller code itself runs inside an async context.

NodeJs : Multiple loop of HTTP requests execute simultaneously

nodejs multiple http requests in loop
As per the above question,it was answered how to perform a loop of http requests with an array of urls which works fine.But what I am trying to achieve is to perform another loop of http requests which should be done only after the completion of the first loop (i.e) it should wait for the first loop of http requests to complete.
// Import http
var http = require('http');
// First URLs array
var urls_first = ["http://www.google.com", "http://www.example.com"];
// Second URLs array
var urls_second = ["http://www.yahoo.com", "http://www.fb.com"];
var responses = [];
var completed_requests = 0;
function performHTTP(array) {
for (i in urls_first) {
http.get(urls[i], function(res) {
responses.push(res);
completed_requests++;
if (completed_requests == urls.length) {
// All download done, process responses array
console.log(responses);
}
});
}
}
In the above snippet ,i have added another array of urls.I wrapped the for inside a function to change the array each time called.Since i have to wait for the first loop to complete, i tried async/await like below.
async function callMethod() {
await new Promise (resolve =>performHTTP(urls_first))) // Executing function with first array
await new Promise (resolve =>performHTTP(urls_second))) // Executing function with second array
}
But in this case both the function calls get executed simultaneously(i.e)it does not wait for the first array execution to complete .Both execution happens simultaneously,which i need to happen only after the completion of one.
You need to make your request inside a Promise :
function request(url) {
return new Promise((resolve, reject) => {
http.get(url, function(res) {
// ... your code here ... //
// when your work is done, call resolve to make your promise done
resolve()
});
}
}
And then resolve all your requests
// Batch all your firts request in parallel and wainting for all
await Promise.all(urls_first.map(url => request(url)));
// Do the same this second url
await Promise.all(urls_second.map(url => request(url)));
Note, this code is not tested and may contains some mistake, but the main principle is here.
More information about Promise : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise
Check out how to use .then() to call second performHttp right after the first one is completed.
You can call services using eachSeries.
https://www.npmjs.com/package/async-series
series([
function(done) {
console.log('First API Call here')
done() // if you will pass err in callback it will be caught in error section.
},
function(done) {
console.log('second API call here')
done()
},
function(done) {
// handle success here
}
], function(err) {
console.log(err.message) // "another thing"
})

Cannot perform http request with http module nodejs azure function

I am trying to make a http request with node module http on an azure function with javascript and for some reason http.request is not receiving data(no error printed its like the requested is blocked). There is anything wrong with azure configuration or the code?Am i missing something really obvious?
Code is running fine on local js file
context.log('JavaScript timer trigger function ran!', timeStamp);
printed as expected but context.log("SUCCESS", JSON.parse(data)); not printed at all.
Also tryed different libraries (request, axios) with no success
module.exports = async function (context, myTimer) {
var timeStamp = new Date().toISOString();
if (myTimer.IsPastDue)
{
context.log('JavaScript is running late!');
}
context.log('JavaScript timer trigger function ran!', timeStamp);
const http = require('http');
http.get('http://myAPIurl', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
context.log("SUCCESS", JSON.parse(data));
});
}).on("error", (err) => {
context.log("ERROR: " + err.message);
});
}
I rewrote your code using Axios. It has support for async/await out of the box, and simplifies a lot of the code to function as your would expect since it makes asynchronous code perform like synchronous code.
I think the main issue you may have been running into it that everything in JavaScript is async. As a result, the Azure Function runtime was exiting before your async function finished since it didn't block like synchronous code would when making the HTTP request. If you want to use callbacks, you need to call context.done() inside your callback function so the Azure Function doesn't exit before the callback is completed. By using async/await, you can guarantee that your code will block on the HTTP request until it receives a response or timeout. In the example below, Axios will return a response object that includes data as an element. I'm extracting data with a deconstruction operation, which allows me to log data.
const axios = require('axios');
const url = 'https://google.com';
module.exports = async function (context, myTimer) {
try {
const { data } = await axios.get(url);
context.log(data);
// do something with the data
return data;
} catch (err) {
context.log(err);
// do something with the error
}
context.done();
}
To Install a Package on an Azure Function
Go to the Azure Portal; select your Azure Function App
Select Platform Features. Then Advanced Tools (Kudu) under Development Tools
Using the file explorer, navigate to site/wwwroot/. You should see a package.json file in there, and several folders each with the name of your functions.
From that directory, run npm install --save axios
To confirm it worked, edit your package.json with the pencil icon. axios should be listed under dependencies json element
I've kept your code callback based.
I removed the async moniker from the definition and added a call to context.done (this signals the functions host when your function has ended) in your resp.end handler
module.exports = function (context, myTimer) {
var timeStamp = new Date().toISOString();
if (myTimer.IsPastDue)
{
context.log('JavaScript is running late!');
}
context.log('JavaScript timer trigger function ran!', timeStamp);
const https = require('https');
https.get('https://raw.githubusercontent.com/LearnWebCode/json example/master/animals-1.json', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
context.log("SUCCESS", JSON.parse(data));
context.done(null, data);
});
}).on("error", (err) => {
context.log("ERROR: " + err.message);
});
}
Another option would be to keep the function as async but you'd need to replace the callbacks with promise based calls. In some scenarios this can be achieved by wrapping them using util.promisify and then calling them with the await keyword

How do we sent data to local variable in node js async with DB Calls

I am new to javascript async programming and i have a basic issue where I have a Set of code where i am doing two separate DB calls on basis of request body params .
These are two methods which does a DB Call and returns a Promise
validateExam
validateUserExists
I want to store resullts from async call to this myExam variable and then return it in response .
getExam: function(req, res) {
var myExam = {};
var coupon = req.body.coupon;
var email = req.body.email;
async.series([
function(callback) {
validateExam(coupon)
.then(function(success) {
callback(null, success);
});
},
function(callback) {
validateUserExists(email)
.then(function(result) {
callback(null, result);
})
}
], function(error, results) {
myExam.res = results;
});
res.json({
"status": 400,
"message": myExam
});
},
You can't return an asynchronously retrieved value from your function. Your function returns BEFORE the async operation is even done. Instead, you need to communicate the return value back to the caller via either a returned promise or by passing in a callback that you can call when the async operation is done. For more info on the details of that, see: How do I return the response from an asynchronous call?.
In addition, using the async library to manage two promise operations is very odd. Promises have all the tools built in themselves to manage asynchronous operations so if your core operations are already returning promises, you should just use those promises directly and not involve the async library.
In looking at your code, it appears that validating the exam and validating the user are independent operations and you can run them in a parallel and use Promise.all() to know when both promises are done.
You can do something like this:
getExam: function(req, res) {
var coupon = req.body.coupon;
var email = req.body.email;
Promise.all([validateExam(coupon), validateUserExists(email)]).then(function(results) {
// results is a two element array that contains the two validation results
// send your response here based on the results array (not clear to me exactly what you want here)
res.json(...);
}).catch(function(err) {
// return some sort of error response here
res.status(500).json(...);
});
},

How to force a program to wait until an HTTP request is finished in JavaScript?

Is there a way in JavaScript to send an HTTP request to an HTTP server and wait until the server responds with a reply? I want my program to wait until the server replies and not to execute any other command that is after this request. If the HTTP server is down I want the HTTP request to be repeated after a timeout until the server replies, and then the execution of the program can continue normally.
Any ideas?
Thank you in advance,
Thanasis
EDIT: Synchronous requests are now deprecated; you should always handle HTTP requests in an async way.
There is a 3rd parameter to XmlHttpRequest's open(), which aims to indicate that you want the request to by asynchronous (and so handle the response through an onreadystatechange handler).
So if you want it to be synchronous (i.e. wait for the answer), just specify false for this 3rd argument.
You may also want to set a limited timeout property for your request in this case, as it would block the page until reception.
Here is an all-in-one sample function for both sync and async:
function httpRequest(address, reqType, asyncProc) {
var req = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
if (asyncProc) {
req.onreadystatechange = function() {
if (this.readyState == 4) {
asyncProc(this);
}
};
}
req.open(reqType, address, !(!asyncProc));
req.send();
return req;
}
which you could call this way:
var req = httpRequest("http://example.com/aPageToTestForExistence.html", "HEAD"); // In this example you don't want to GET the full page contents
alert(req.status == 200 ? "found!" : "failed"); // We didn't provided an async proc so this will be executed after request completion only
You can perform a synchronous request. jQuery example:
$(function() {
$.ajax({
async: false,
// other parameters
});
});
You should take a look at jQuery's AJAX API. I highly recommend using a framework like jQuery for this stuff. Manually doing cross-browser ajax is a real pain!
You can use XMLHttpRequest object to send your request. Once request is sent, you can check readyState property to identify current state. readyState will have following different states.
Uninitialized - Has not started loading yet
Loading - Is loading
Interactive - Has loaded enough and the user can interact with it
Complete - Fully loaded
for example:
xmlhttp.open("GET","somepage.xml",true);
xmlhttp.onreadystatechange = checkData;
xmlhttp.send(null);
function checkData()
{
alert(xmlhttp.readyState);
}
hope this will help
For the modern browser, I will use the fetch instead of XMLHttpRequest.
async function job() {
const response = await fetch("https://api.ipify.org?format=json", {}) // type: Promise<Response>
if (!response.ok) {
throw Error(response.statusText)
}
return response.text()
}
async function onCommit() {
const result = await job()
// The following will run after the `job` is finished.
console.log(result)
}
fetch syntax
an examples
<button onclick="onCommit()">Commit</button>
<script>
function onCommit() {
new Promise((resolve, reject) => {
resolve(job1())
}).then(job1Result => {
return job2(job1Result)
}).then(job2Result => {
return job3(job2Result)
}).catch(err => { // If job1, job2, job3, any of them throw the error, then will catch it.
alert(err)
})
}
async function testFunc(url, options) {
// options: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
const response = await fetch(url, options) // type: Promise<Response>
if (!response.ok) {
const errMsg = await response.text()
throw Error(`${response.statusText} (${response.status}) | ${errMsg} `)
}
return response
}
async function job1() {
console.log("job1")
const response = await testFunc("https://api.ipify.org?format=json", {})
return await response.json()
}
async function job2(job1Data) {
console.log("job2")
console.log(job1Data)
const textHeaders = new Headers()
textHeaders.append('Content-Type', 'text/plain; charset-utf-8')
const options = {"headers": textHeaders}
const response = await testFunc("https://api.ipify.org/?format=text", options)
// throw Error(`test error`) // You can cancel the comment to trigger the error.
return await response.text()
}
function job3(job2Data) {
console.log("job3")
console.log(job2Data)
}
</script>
For this you can start loader in javascript as soon as page starts loading and then you can close it when request finishes or your dom is ready.
What i am trying to say, as page load starts, start a loader . Then page can do multiple synchronous request using ajax , until and unless you didn't get response, do not close close loader.
After receiving the desired in response in final call, you can close the loader.
I have a similar situation in an game built with Three.js and Google Closure. I have to load 2 resources, Three and Closure do not allow me to make these synchronous.
Initially I naively wrote the following:
main() {
...
var loaded=0;
...
// Load Three geometry
var loader = new THREE.JSONLoader();
loader.load("x/data.three.json", function(geometry) {
...
loaded++;
});
// Load my engine data
goog.net.XhrIo.send("x/data.engine.json", function(e) {
var obj = e.target.getResponseJson();
...
loaded++;
});
// Wait for callbacks to complete
while(loaded<2) {}
// Initiate an animation loop
...
};
The loop that waits for the callbacks to complete never ends, from the point of view of the loop loaded never get incremented. The problem is that the callbacks are not fired until main returns (at least on Chrome anyway).
One solution might be to have both callbacks check to see if it's the last to complete, and them go on to initiate the animation loop.
Another solution - perhaps a more direct answer to what you are asking (how to wait for each load before initiating another) - would be to nest the callbacks as follows:
// Load Three geometry
var loader = new THREE.JSONLoader();
loader.load("x/data.three.json", function(geometry) {
...
// Load my engine data
goog.net.XhrIo.send("x/data.engine.json", function(e) {
var obj = e.target.getResponseJson();
...
// Initiate an animation loop
...
});
});
};
This is an old question but wanted to provide a different take.
This is an async function that creates a promise that resolves with the Http object when the request is complete. This allow you to use more modern async/await syntax when working with XMLHttpRequest.
async sendRequest() {
const Http = new XMLHttpRequest();
const url='http://localhost:8000/';
Http.open("GET", url);
Http.send();
if (Http.readyState === XMLHttpRequest.DONE) {
return Http;
}
let res;
const p = new Promise((r) => res = r);
Http.onreadystatechange = () => {
if (Http.readyState === XMLHttpRequest.DONE) {
res(Http);
}
}
return p;
}
Usage
const response = await sendRequest();
const status = response.status;
if (status === 0 || (status >= 200 && status < 400)) {
// The request has been completed successfully
console.log(response.responseText);
} else {
// Oh no! There has been an error with the request!
console.log(`Server Error: ${response.status}`)
}
For those using axios, you can wrap it in an async iife and then await it:
(async () => {
let res = await axios.get('https://example.com');
// do stuff with the response
})();
Note, I haven't done any error checking here.

Categories