How to write a Synchronous function in Node using Promises - javascript

1.How to write Promises Synchronously in Node so that I can get my desired output. I'm a newbie and would appreciate any help/suggestion.
// This is my core function
var compareData = function(userIdArray) {
return new Promise(function(resolve, reject) {
var missingArray = new Array();
userIdArray.forEach(function(id) {
var options = {
method: 'POST',
url: 'http://localhost:6006/test1',
headers:{
'content-type': 'application/json' },
body: { email: id },
json: true
};
request(options, function (error, response, body) {
missingArray.push(body);
});
});
resolve(missingArray);
});
}
//I'm calling my function here
compareData(userIdArray)
.then(function(missingArray){
console.log("The Body is: "+ missingArray);
});
/* I expect the console.log to print the missingArray with data from my POST call,
but it prints an empty array. Can someone please tell me how to do this synchronously.
I'm pretty new to Node and finding it difficult to understand.*/

If you don't want to use external libraries as per #Thomas answer, you can use native Promises directly - and it's not too much more verbose
var compareData = function compareData(userIdArray) {
return Promise.all(userIdArray.map(function (id) {
return new Promise(function (resolve, reject) {
var options = {
method: 'POST',
url: 'http://localhost:6006/test1',
headers: {
'content-type': 'application/json'
},
body: {
email: id
},
json: true
};
return request(options, function (error, response, body) {
error ? reject(error) : resolve(body);
});
});
}));
};
compareData(userIdArray)
.then(function (missingArray) {
console.log("The Body is: " + missingArray);
});
Or, as this is node, which can handle more modern code:
var compareData = userIdArray =>
Promise.all(userIdArray.map(id =>
new Promise((resolve, reject) =>
request({
method: 'POST',
url: 'http://localhost:6006/test1',
headers: {
'content-type': 'application/json'
},
body: {
email: id
},
json: true
}, (error, response, body) => error ? reject(error) : resolve(body))
)
));
compareData(userIdArray)
.then(missingArray =>
console.log("The Body is: "+ missingArray)
);

with bluebird and request-promise:
var Promise = require('bluebird');
var request = require('request-promise');
var compareData = function(userIdArray) {
//Promise.all():
//takes an array of promises (and/or values),
//returns a promise of the resolved array
return Promise.all(
userIdArray.map(function(id){
return request({
method: 'POST',
url: 'http://localhost:6006/test1',
headers: { 'content-type': 'application/json' },
body: { email: id },
json: true
});
})
);
}
is there anything that needs further explanation?

Without libraries, and assuming request doesn't already return a promise
var compareData = function(userIdArray) {
return Promise.all(
userIdArray.map(function(id) {
var options = {
method : 'POST',
url : 'http://localhost:6006/test1',
headers : { 'content-type': 'application/json' },
body : { email: id },
json : true
};
return new Promise(function(resolve, reject) {
request(options, function(error, response, body) {
if (error) {
reject();
} else {
resolve(body);
}
});
});
})
);
}
compareData(userIdArray).then(function(missingArray) {
console.log(missingArray);
});

Related

defining a await for the function in Node.js

I am trying to get the response data from 3 APIs which is depended on each other. when I call each external API the response has an array of external APIs, as I need all the 3 external APIs responses.
Here is the code:
const options = {
JSON: true,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
"Authorization": process.env.authID,
},
body: {},
uri: ''
};
app.post('/sync', async (req, res) => {
try {
let getFirstAPI = Object.assign({}, options);
getFirstAPI.uri = 'https://abcde'; // first API
request(getFirstAPI, function (err, httpResponse, body) {
Promise.all(body.projects.map((prct) => getVuln(prct))).then(rs => {
res.JSON({ message: rs });
});
});
}
});
async function getVuln(prodId) {
try {
let getSecondAPI= Object.assign({}, options);
getSecondAPI.uri = 'abcd' + prodId.id + '/abc'; //Second API
return await new Promise((resolve, reject) => {
request(getSecondAPI, function (err, httpResponse, body) {
let final = {
products: [],
dataList: []
}
final.products.push({
product_id: prodId.id,
product_name: prodId.abc,
product_ver: prodId.wdc
})
body.issues.forEach(csa => {
final.dataList.push({
product_id: prodId.id,
version: csa.Versions,
name: csa.Name,
pathJsonValue:await getPathfun(csa.link) //Here i am getting error "SyntaxError: Unexpected identifier"
});
});
resolve(final);
});
})
}
}
function getPathfun(urlStr) {
let getPathUrl = Object.assign({}, options);
getPathUrl.url = urlStr;
return new Promise((resolve, reject) => {
request(getPathUrl, function (err, httpResponse, body) {
resolve(body);
});
});
}
at the "pathJsonValue" I am getting an error while running the code, if I remove the await then "pathJsonValue" is empty. I have attached the error image below. please help me with this issue.
request(getSecondAPI, function (err, httpResponse, body) {
Callback must be an ASYNC function to have the possibility to use 'await' keyword inside it.
'await' keyword doesnt work in "forEach" method. Use for...of loop instead.

How do I use async-await properly in the following situation?

I'm struggling with async await block. I have looked the multiple resources on the net but I just can't understand what I'm doing wrong here :
app.post('/api/my-api', async (req, res, next) => {
try {
filecontent = req.files.userFile.data.toString('utf8')
console.log("decoded file : ", filecontent);
let encoded_file = new Buffer(filecontent).toString('base64');
var apiClass = new RPC("http://localhost:1006", "my-api");
//the asynchronous function :
const answ = await apiMethod.call("api", [{"file" : encoded_file, "fileName":req.files.userFile.name}], res.json);
//the code I'd like to execute only after the previous function has finished :
console.log("answer : ", answ);
console.log("answering...");
res.json(answ);
} catch (err) {
console.log(err);
}
Apparently my console.log are executed before the the await line is done. I can tell because there's a console.log() in the asynchronous function too, and my res.json is sent before I receive answ.
How do I make sure the asynchronous function finishes before the rest of the code ?
Edit : here is the apiMethod.call function :
call(id, params) {
let options = {
url: this.url,
method: "post",
headers:
{
"content-type": "text/plain"
},
body: JSON.stringify( {"jsonrpc": "2.0", "id": id, "method": this.procedure, "params": params })
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error('An error has occurred: ', error);
} else {
console.log('Post successful: response: ', body);
}
});
}
Issue is in call function. Since it has async code (request call), it should be wrapped in promise which should resolve from callback function of request.
Updating call function to something like below should help:
function call(id, params) {
return new Promise((resolve, reject) => {
let options = {
url: this.url,
method: "post",
headers: {
"content-type": "text/plain",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: id,
method: this.procedure,
params: params,
}),
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error("An error has occurred: ", error);
reject(error);
} else {
console.log("Post successful: response: ", body);
resolve(body);
}
});
})
}

Save token and use it on post request nodejs

I 'm making a node project and i 'm having trouble when i try to use token result for get request and use it in another get..
how can i use?
thanks!
this is my code:
app.js
.get('/api/v1/token', tokenController.getToken)
.get('/api/v1/search', searchController.getSearch)
Tokencontroller.js
const eventService = require('../services/eventService')
function getToken(req, res){
eventService.getToken()
.then(response => {
console.log(response.body)
const token = response.body.access
console.log(token)
res.send(token)
})
.catch(error => {
console.log('token error')
next(error)
})
}
module.exports = {getToken}
eventService.js
module.exports = {
getToken() {
return new Promise(function (resolve, reject) {
var payload = {
username: 'jon#mail.com',
password: "1111",
grant_type: 'password',
};
request.post({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + new Buffer.from('user-one' + ':' + '000000000').toString('base64'),
'Accept': 'application/json'
},
url: 'myurl/oauth/token',
form: payload
}, function (error, response, body) {
if (error) {
reject(error)
} else {
resolve({ response , body })
}
});
})
}
now i got the token and i need to pass it to getSearch ..
token response:
{"access_token":"123456","token_type":"bearer","refresh_token":"12345"}
searchController.js
function getSearch(req, res){
eventService.getSearch()
.then(response => {
res.send(response)
})
.catch(error => {
console.log(error)
next(error)
})
}
eventService getSearch
getSearch() {
return new Promise(function (resolve, reject) {
request.post({
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + '123456' //==> token access_token propertie
},
url: 'myurlcontent/search',
}, function (error, response, body) {
if (error) {
reject(error)
} else {
resolve({ response, body })
}
});
})
}

Request Promise not returning a value (Node JS)

I have been trying to get this to work for while now, my request promise is not returning the streamer it has found. When i console.log(streamer) inside of the .then part of the request it works. Any ideas?
function getStreamerByName(name){
var streamer;
var options = {
url: "https://api.twitch.tv/helix/users?login=" + name,
method: 'GET',
headers: {
'Client-ID': 'CLIENT_ID',
}
};
requestP(options)
//SPREAD
.spread(function(res, body) {
streamer_data = JSON.parse(body);
})
//THEN
.then(function (body) {
streamer = ({
twitch_id: streamer_data["data"][0]["id"],
name: streamer_data["data"][0]["display_name"],
image: streamer_data["data"][0]["profile_image_url"],
description: streamer_data["data"][0]["description"]
});
Streamer.create(streamer);
return streamer;
})
//CATCH
.catch(function (err) {
console.log(err);
return streamer;
});
}
You need to return the promise chain:
function getStreamerByName(name){
var streamer;
var options = {
url: "https://api.twitch.tv/helix/users?login=" + name,
method: 'GET',
headers: {
'Client-ID': 'CLIENT_ID',
}
};
//A return was added here
return requestP(options)
//SPREAD
.spread(function(res, body) {
streamer_data = JSON.parse(body);
})
//THEN
.then(function (body) {
streamer = ({
twitch_id: streamer_data["data"][0]["id"],
name: streamer_data["data"][0]["display_name"],
image: streamer_data["data"][0]["profile_image_url"],
description: streamer_data["data"][0]["description"]
});
Streamer.create(streamer);
return streamer;
})
//CATCH
.catch(function (err) {
console.log(err);
return streamer;
});
}
When you all this function externally, you'll also need to use a .then() to get the result or use an async function and await.
so
async caller() {
var value = await getStreamerByName('stuff')
}
or
caller() {
getStreamerByName('stuff').then((result) => { //do stuff })
}
I'm not sure if this is what you mean but i just tried this and its not getting the streamer still, its coming up with an error message saying:
TypeError: Cannot read property 'then' of undefined
function getStreamers() {
getStreamerByName(search).then((streamer) => {
console.log(streamer)
})
}
getStreamers();
async function getStreamerByName (name) {
return new Promise(function (resolve, reject) {
Your code
});
}
Var myStreamer = getStreamerByName ('myStreamer');

How to solve promise issue?

I am new to Promise concepts and trying to wrap my head around but now I`` am confused here
const request = require("request");
const cheerio = require("cheerio");
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var url = require("url");
module.exports = {
resturant: resturant,
};
var resturanttables = [];
function resturant(url, day) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, html) {
if (error) {
return reject(error);
} else if (response.statusCode !== 200) {
return reject("Something is wrong! CINEMA")
}
httplogin("zeke", "coys", url, day);
console.log(resturanttables, "i am here");
resolve(resturanttables);
});
});
}
function httpafterlogin(url, cookie, day) {
request.get({
headers: {
'content-type': 'text/html',
'Cookie': cookie
},
url: url,
},
function(error, response, body) {
console.log(day)
var $ = cheerio.load(body);
if (day === "Friday") {
$(".WordSection2 p span ").each(function(li) {
// console.log(day, $(this).text())
resturanttables.push($(this).text());
console.log(resturanttables, "nside");
});
} else if (day === "Saturday") {
$(".WordSection4 p span").each(function(li) {
resturanttables.push($(this).text())
});
} else {
$(".WordSection6 p span").each(function(li) {
resturanttables.push($(this).text())
});
}
});
}
function httplogin(username, password, urls, day) {
request.post({
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
url: urls,
form: {
"username": username,
"password": password
}
}, function(error, response, body) {
var cookie = response.caseless.dict['set-cookie'][0];
var location = response;
console.log(response.statusCode);
cookie = cookie.substring(0, cookie.indexOf(';'));
// httpafterlogin('http://vhost3.lnu.se:20080/dinner/'+response.headers.location, cookie);
var newurls = url.resolve(urls, response.headers.location)
httpafterlogin(newurls, cookie, day);
// console.log(response.headers, "jdjdjjdjdjjdjdjdjjdjjdjdj")
});
}
and then I call the function
loadPage.resturant("http://vhost3.lnu.se:20080/dinner/login", "Friday").then(function(data) {
console.log(data, "did it work now ")
})
the problem is that it returns the empty array. But when i tried to check and console.log in the afterlogin function and i could see that the array was actually filled, but that code runs after the promise has been resolved.
IN SHORT: how can I bound the resolve in restaurant promise not to send the data until the login function is completed?
in other words how can i get the filled array with information from afterlogin funtion?
rewrite httplogin and httpafterlogin to return promises:
function httpafterlogin (url, cookie, day) {
return new Promise(function (resolve, reject) {
request.get({
headers: {
'content-type': 'text/html',
'Cookie': cookie
},
url: url
}, function (error, response, body) {
if (error) {
reject(error);
} else {
resolve(body);
}
});
}).then(function (body) {
console.log(day);
var $ = cheerio.load(body);
if (day === "Friday") {
$(".WordSection2 p span").each(function (li) {
// console.log(day, $(this).text());
resturanttables.push($(this).text());
console.log(resturanttables, "nside");
});
} else if (day === "Saturday") {
$(".WordSection4 p span").each(function (li) {
resturanttables.push($(this).text());
});
} else {
$(".WordSection6 p span").each(function(li) {
resturanttables.push($(this).text());
});
}
});
}
function httplogin(username, password, urls, day) {
return new Promise(function (resolve, reject) {
request.post({
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
url: urls,
form: {
"username": username,
"password": password
}
}, function(error, response, body) {
if (error) {
reject(error);
} else {
resolve(response);
}
});
}).then(function (response) {
var cookie = response.caseless.dict['set-cookie'][0];
var location = response;
console.log(response.statusCode);
cookie = cookie.substring(0, cookie.indexOf(';'));
var newurls = url.resolve(urls, response.headers.location)
return httpafterlogin(newurls, cookie, day);
});
}
then use .then like rsp suggested:
function resturant(url, day) {
return new Promise(function(resolve, reject) {
request(url, function(error, response, html) {
if (error) {
return reject(error);
} else {
resolve(response);
}
})
}).then(function (response) {
if (response.statusCode !== 200) {
throw new Error("Something is wrong! CINEMA");
}
return httplogin("zeke", "coys", url, day)
}).then(function () {
console.log(resturanttables, "i am here");
return resturanttables;
});
}
this way, the block containing resolve(restautanttables) will not get called until httplogin completes
Use promises throughout your code - you can simplify your code using the request-promise package in place of the request package. All requests become promises and the code is easier to read and maintain.
const rp = require("request-promise");
const cheerio = require("cheerio");
const url = require("url");
function resturant(url, day) {
rp(url)
.then(function(){
// URL returned a 200 response
// so attempt to perform login
httplogin("zeke", "coys", url, day)
.then(function (data) {
// Promise is resolved here
return data;
});
})
.catch(function(error){
// just throwing the error
throw error;
});
}
function httplogin(username, password, urls, day) {
var options = {
headers: {
"content-type": "application/x-www-form-urlencoded"
},
uri: urls,
form: {
username: username,
password: password
},
method: "POST",
resolveWithFullResponse: true
};
rp(options)
.then(function (response) {
// POST succeeded
// grab the cookie
var cookie = response.caseless.dict['set-cookie'][0]
.substring(0, cookie.indexOf(';'));
// get new url string
var newurls = url.resolve(urls, response.headers.location);
httpafterlogin(newurls, cookie, day)
.then(function (tables) {
return tables;
})
.catch(function (error) {
// just throwing the error
throw error;
});
})
.catch(function (error) {
// Login failure
// just throwing the error
throw error;
});
}
function httpafterlogin(url, cookie, day) {
var options = {
headers: {
"content-type": "text/html",
"Cookie": cookie
},
uri: url,
transform: function (body) {
return cheerio.load(body);
}
};
rp(options)
.then(function ($) {
// body has been transformed and
// can now be processed with jQuery
// initialise the tables array
var tables = [];
// DRY code
// set default selector
var selector = ".WordSection6 p span";
// change the selector for Friday/Saturday
if (day === "Friday") {
selector = ".WordSection2 p span ";
} else if (day === "Saturday") {
selector = ".WordSection4 p span";
}
// process the selected section
$( selector ).each(function(li) {
tables.push($(this).text())
});
// crawling complete
return tables;
})
.catch(function (error) {
// Crawling failure
// just throwing the error
throw error;
});
}
If you don't want the promise to get resolved before the login is completed then you will either have to make your httplogin function take a callback and run it like this:
httplogin("zeke", "coys", url, day, function (err) {
if (err) {
reject(err);
} else {
resolve(resturanttables);
}
});
or make it return a promise and run it for example like this:
httplogin("zeke", "coys", url, day).then(function () {
resolve(resturanttables);
}).catch(function (err) {
reject(err);
});
There are more ways to do it with promises but this is the simplest way.
Either way you have to make your httplogin function signal its completion by either calling the callback that it takes as an argument or resolving the promise that it returns.

Categories