I'm playing around with promises and I'm having trouble with an asynchronous recursive promise.
The scenario is that i need to check for token validation inside a function and if the token expires i need to call the function again.
Below is the code i've done so far:
module.exports={
frstFun:()=>{...
},
secondFun:(param)=>{
return new Promise((resolve,reject)=>{
request({
url:url+'myapi',
qs:{q:param,acc_token:token},
method:'GET',
},function(err,response,body){
if(error){
//here i need to call secondFun() method..
}
});
})
}
};
I tried calling this.secondFun() but it's giving error secondFun() is not a method.
I'm not sure if a recursive retry is the best way to do this, but you could pull the function definition out of the exports object:
let secondFun = (param) => {
return new Promise((resolve, reject) => {
request({
url: url + 'myapi',
qs: { q: param, acc_token: token },
method: 'GET',
}, function (err, response, body) {
if (err) {
// secondFun(...)
}
});
})
};
module.exports = {
frstFun: () => { ... },
secondFun: secondFun
};
Related
I have an array of ssn number and I have two api list in which I need to pass ssn number as request json so I need to call both api inside ssn loop so I pass ssn to json request during call both api but code is not work properly both api call at a time simulteniously, Where I need to call both api one by one.
Both API details and code are as follow
My Code:
let ssn = [460458524, 637625452, 453311896, 635285187, 455791630, 642348377, 463590491, 450730278, 641201851, 379965491];
async function getCRCDetails() {
ssn.forEach(function (item) {
if(item){
let CBCOptions = {
'method': 'POST',
'url': 'https://loanboard.houstondirectauto.com/api/Report',
'headers': {
'Content-Type': 'application/json',
'Cookie': 'ci_session=udmojmlc5tfl3epbrmtvgu6nao2f031p'
},
body: JSON.stringify({
"token": loantoken,
"action": "CBCReport",
"variables": {
ssn: item
}
})
}
request(CBCOptions, function (error, response) {
console.log(item);
console.log("CBCOPtion ", CBCOptions);
if (error) throw new Error(error);
result = (JSON.parse(response.body));
console.log("Result =", result);
CRCReport.push(result);
})
let EmployerInfoOptions = {
'method': 'POST',
'url': 'https://loanboard.houstondirectauto.com/api/Report',
'headers': {
'Content-Type': 'application/json',
'Cookie': 'ci_session=udmojmlc5tfl3epbrmtvgu6nao2f031p'
},
body: JSON.stringify({
"token": loantoken,
"action": "getEmployerInfo",
"variables": {
ssn: item
}
})
}
request(EmployerInfoOptions, function (error, response) {
console.log(response.body);
})
}
Here I need to call API request one by one.Anyone Guide me please.
I prefer use async await method for this situation
you need install and require async and request-promise
after that :
const request = require("request-promise");
const async = require("async");
let ssn = [460458524, 637625452, 453311896, 635285187, 455791630, 642348377, 463590491, 450730278, 641201851, 379965491];
async function getCRCDetails() {
//like a forEache
async.eachSeries(ssn, async (item) => {
let CBCOptions = {
method: "POST",
url: "https://loanboard.houstondirectauto.com/api/Report",
headers: {
"Content-Type": "application/json",
Cookie: "ci_session=udmojmlc5tfl3epbrmtvgu6nao2f031p",
},
body: JSON.stringify({
token: loantoken,
action: "CBCReport",
variables: {
ssn: item,
},
}),
};
let EmployerInfoOptions = {
method: "POST",
url: "https://loanboard.houstondirectauto.com/api/Report",
headers: {
"Content-Type": "application/json",
Cookie: "ci_session=udmojmlc5tfl3epbrmtvgu6nao2f031p",
},
body: JSON.stringify({
token: loantoken,
action: "getEmployerInfo",
variables: {
ssn: item,
},
}),
};
try {
let resultCBCOptions = await request(CBCOptions);
let EmployerInfoOptions = await request(EmployerInfoOptions);
console.log(resultCBCOptions)
console.log(EmployerInfoOptions)
//do pushing resultCBCOptions
//do pushing EmployerInfoOptions
} catch (error) {
console.log(error);
}
},
() => {
console.log("finished");
}
);
}
In Node the request methods that you are using are asynchronous. Meaning the runner (server) that runs the code does not wait for the request to finish and just continues to execute the next lines.
One thing that you can do is,
request.post(params).on("response", function(response) {
//....Do stuff with your data you recieve
// Make the next call here
request.post(params).on("response"), function() {
// Here you will have the second call's results
}
})
This ensures that both the API calls happen in order and only after the first one finishes.
Note:
The request library that you are using has been deprecated back in 2020. See https://github.com/request/request Hence, I would suggest you use other libraries like the standard https or http library that is shipped with node or you can use axios.
If you use a forEach loop without awaiting the results, you'll execute them all at the same time. Moreover, request library is kind of old and you need to convert its functions to return a promise.
Here's how I would do it.
const ssn = [1,2,3,4];
function download(item) {
return new Promise(function(resolve, reject) {
let options = {}; // construct your request
request(options, function (error, response) {
if(error) {
return reject(error);
}
resolve(response);
})
}
}
ssn = ssn.map(async function(item) {
let res = await download(item);
// process the result
return res;
});
You can also use the Bluebird library or get another client library such as got or axios.
I have problems with the synchronous and asynchronous functions on NodeJS, here is my problem,
I have 4 functions, a global variable (users), and a rendering function to have the html page.
the scraper1 and scraper2 function can be performed asynchronously, they scrape a website and fill in the global user variable, and the data_selector1 cannot be done unless scraper1 has done the same for data_selector2 with the scraper2 functions.
1-I need that scraper1 and scraper2 work asynchronously and fill the available users, and only render the HTML page if the 2 scrapers have finished their work.
2-I need an animation to be displayed in the browser during the drilling process, how can I do this
Here is what I tried ..
var express = require('express');
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
var router = express.Router();
/* globale variable to populate */
var users = {
'name':null,
'age':null,
};
//function of scraping link1
function scarper1(callback){
console.log("-------------scraper---------");
var url = 'link1';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function(error, response, body) {
if (error) throw error;
// call the data-selector1 after scraper are finish rendering
data_selector1(body);
}
);
}
//function of scraping link2
function scarper2(callback){
console.log("-------------scraper2---------");
var url = 'link2';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function(error, response, body) {
if (error) throw error;
// call the data-selector2 after scraper are finish rendering
data_selector2(body);
}
);
}
function data_selector1(body)
{
console.log("-------------data-selector---------");
const $ = cheerio.load(body);
$("div[class='.user']").each(function(i,elem){
users['name'] =$(elem).find('span[class=".name]').text();
users['age'] =$(elem).find('span[class=".age]').text();
});
}
function data_selector2(body)
{
console.log("-------------data-selector2---------");
const $ = cheerio.load(body);
$("ul[class='.user']").each(function(i,elem){
users['name'] =$(elem).find('li[class=".name]').text();
users['age'] =$(elem).find('li[class=".age]').text();
});
}
/* GET home page. */
router.post('/recherche', function(req, res, next) {
// i dont know how to make it here to say that scraper1 and scraper2 can be executed async and to render page after that the two scraper are finished
// and while scraper are working to display animation in the client
scarper1(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
});
You can use promise.all() to do that but before you can use it, you need to promisify your functions:
function scarper1(callback) {
return new Promise((resolve, reject) => {
console.log("-------------scraper---------");
var url = 'link1';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector1 after scraper are finish rendering
data_selector1(body);
resolve('Done successfully');
}
);
});
}
function scarper2(callback) {
return new Promise((resolve, reject) => {
console.log("-------------scraper2---------");
var url = 'link2';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector2 after scraper are finish rendering
data_selector2(body);
resolve('Done successfully');
}
);
});
}
let scarper1 = scarper1(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
let scarper2 = scarper2(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
Promise.all([scarper1, scarper2]).then(function(values) {
console.log(values);
});
For more about promise.all check this docs.
An even better approach is using async.eachLimit() to loop through requests (asynchronously) but first you need to install async package then merge both scraper functions:
const async = require("async");
let urls = [
'link1',
'link2'
]
async.eachLimit(urls, 2, (url) => {
console.log("-------------scraper---------");
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector1 after scraper are finish rendering
if(url == 'link1')
data_selector1(body);
else
data_selector2(body);
resolve('Done successfully');
}
);
}, (err) => {
console.log("Finished all urls")
});
As the title says what I was trying to do is make a universal function to both do GET and POST calls with one function. However, because when sending a GET call it requires the params entry to contain the data, when sending data via POST it requires the data entry (if I'm not mistaken).
I currently have the following function;
function api(method, call, params){
return new Promise(function(resolve, reject){
axios({
url: call,
method,
params
}).then(function(response) {
var body = response.data;
if(body.status !== 1){
return reject(body.message);
}
resolve(body.response);
}).catch(function(err){
reject(err);
});
});
GET calls work fine as there is the params entry, but for POST calls it stops working. How can I fix this so I have one function to handle both calls?
Another way would be to accept config object as a parameter. Also, you do not need to wrap axios() in a new promiseasaxios()` returns a promise itsef.
function api(config) {
const baseUrl = "http://foo.baz";
const updatedConfig = Object.assign({}, config, {
// If you want to append base url to all api methods.
url: `${baseUrl}${config.url}`
});
return axios(updatedConfig).then(response => {
const body = response.data;
if (body.status !== 1) {
throw new Error(body.message);
}
return body.response;
});
}
Usage:
api({
url: "/user",
method: "get",
params: { id: "xyz" }
}).then(() => { });
or
api({
url: "/tag",
method: "post",
data: { tag: "abc" }
}).then(() => { });
I solved it by just pulling the object into a variable and adding the entry.
Example:
var data = {
url: call,
method
}
if(method.toLowerCase() === 'post'){
data['data'] = params;
}else{
data['params'] = params;
}
axios(data).then(function(response) ...
I'm new to the concept of promises, as well as to JavaScript in general. I'm trying to write a function in Node.js that I can pass a URL to a promise of the results.
I have programmed it two ways. The first does not work, in which I can pass the URL to the function. The second does work, in which the URL is statically defined. The first one does not work because the compiler does not think it is a function for some reason that I can't figure out, why?
THIS WAY DOESN'T WORK as the function getJson is not interpreted by Node as a function:
var options = { method: 'GET',
url: URL, // This will be dynamically filled by the argument to the function getJson
headers: { authorization: 'OAuth realTokenWouldBeHere', Accept: 'application/json' } };
var getJson = function(URL){
return new Promise(function(resolve, reject) {
request(options, function (error, response, body) {
if(error) reject(error);
else {
resolve(JSON.parse(body)); //The body has an array in the jason called Items
}
});
}); // Edited original post. Had two curly braces }}; here by accident, which was why function was not being recognized
};
getJson.then(function(result) {
console.log(result.Items); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
THIS WAY DOES WORK AND I GET BACK AN ARRAY OF ITEMS TO THE CONSOLE. The downside of this is that the URL used is static. The point of what I am trying to do is to chain a bunch of URL's by taking the result of an API, one URL call that then contains he URL of the NextPage of results.
var options = { method: 'GET',
url: 'http://staticURL',
headers: { authorization: 'OAuth realTokenWouldBeHere', Accept: 'application/json' } };
var getJson = new Promise(function(resolve, reject) {
request(options, function(err, response, body) {
if(err) reject(err);
else {
resolve(JSON.parse(body));
}
});
});
getJson.then(function(result) {
console.log(result.Items); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
Try this:
var getJson = function(URL){
var options = {
method: 'GET',
url: URL,
headers: { authorization: 'OAuth realTokenWouldBeHere', Accept: 'application/json' }
};
return new Promise(function(resolve, reject) {
request(options, function (error, response, body) {
if(error) reject(error);
else {
resolve(JSON.parse(body));
}
});
}};
};
And then you can call it:
getJson(theDynamicURLGoesHere).then(function(result) {
console.log(result.Items); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
getJson.then(...)
in your first code block is not correct. It needs to be:
getJson(someURL).then(...)
because in that first code block, getJson is a function so you have to call it to execute it and you need to pass it the desired argument.
In your first code block, getJson is a function that returns a promise when you call and execute the function so you have to call the function in order to get the promise. In the second code block, getJson is already a promise so you can call getJson.then(...).
getJson() is a function, so you need to call it with ():
getJson(URL).then(onFulfilled, onRejected);
The important thing to note is that a promise is an object, you create a new promise using a constructor. Don't try to use a promise as a function - a promise immediately starts executing, .then() just registers an event handler so you're notified when the promise changes state.
I am new to the working with TAPE JS for testing. I have it all setup and working, and it works fine with regular tests. But I am trying to test a unique REST API based product that relies on certain calls to have been made before the next call has the information needed to have a successful call.
So here are the first two calls I am trying to get working:
var SessionId;
test('beginIqsSession', function (assert) {
assert.plan(1);
var requestData = {"ProductDataArray":{"Feid":"GIQNY","AltData":"SaneID:null","Debug":"false","PageId":"1.1"}};
request({
url: 'http://192.168.99.100/Iqs/api.php/beginIqsSession',
method: "POST",
json: requestData
}, function(error, response, json){
if(json.responseDataPayload.SessionId)
{
SessionId = json.responseDataPayload.SessionId;
assert.equal(1,1);
}
});
assert.end();
});
test('validateAddress', function (assert) {
assert.plan(2);
console.log("Retrieving validateAddress response");
var requestData = {"SessionId":SessionId,"ValidateAddressDataArray":{"PropertyStreetNumber":"20671","PropertyStreetName":"mountain vista dr","PropertyCity":"anchorage","PropertyState":"AK","PropertyZipCode":"99577"}};
console.log(SessionId);
request({
url: 'http://192.168.99.100/Iqs/api.php/validateAddress',
method: "POST",
json: requestData
}, function (error, response, body) {
if (!error) {
console.log(body);
}
else {
console.log("error: " + error)
}
});
assert.end();
});
So basically in the code above, I am trying to test beginIqsSession, wait for its response, and store a piece of data from that response that future calls require to be sent in.
in validateAddress you'll see I am trying to pass SessionId in which was returned in the previous call, but because this test is being run at the same time as the previous test, this variable is still empty. How can I make the second call, and all future calls, to wait for the previous call to run?
assert.plan apparently doesn't work in this way.
You could use the Promise API
var SessionId;
let p1 = new Promise((resolve, reject) => {
test('beginIqsSession', function (assert) {
assert.plan(1);
var requestData = {"ProductDataArray":{"Feid":"GIQNY","AltData":"SaneID:null","Debug":"false","PageId":"1.1"}};
request({
url: 'http://192.168.99.100/Iqs/api.php/beginIqsSession',
method: "POST",
json: requestData
}, function(error, response, json){
if(json.responseDataPayload.SessionId)
{
SessionId = json.responseDataPayload.SessionId;
assert.equal(1,1);
resolve(SessionId);
}
});
assert.end();
});
})
p1.then((SessionId) => {
test('validateAddress', function (assert) {
assert.plan(2);
console.log("Retrieving validateAddress response");
var requestData = {"SessionId":SessionId,"ValidateAddressDataArray":{"PropertyStreetNumber":"20671","PropertyStreetName":"mountain vista dr","PropertyCity":"anchorage","PropertyState":"AK","PropertyZipCode":"99577"}};
console.log(SessionId);
request({
url: 'http://192.168.99.100/Iqs/api.php/validateAddress',
method: "POST",
json: requestData
}, function (error, response, body) {
if (!error) {
console.log(body);
}
else {
console.log("error: " + error)
}
});
assert.end();
});
});