unable to resolve inner promise in javascript - javascript

Unable to resolve or come out of inner promise so I can send the data back to client side.
Code:
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) return reject(err);
parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
return reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
return new Promise(function (resolve2, reject2) {
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
return reject2('Error in getting ratings');
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve2(ItemLookup.Items[0].Item[0].CustomerReviews);
});
});
}
console.log('Resolved: ', ItemLookup.Items[0].Item);
resolve(ItemLookup.Items[0].Item);
}
}
});
});
});
}
amazon_router.get('/getDetails', function (req, res, next) {
var amazonApi = decodeURIComponent(req.query.url);
var amzonApiUrl = amazonApi.replace(/\+/g, '%2B');
var isRatings = decodeURIComponent(req.query.ratings);
console.log('');
console.log('amazon api url ', amzonApiUrl);
console.log('');
getAmazonData(amzonApiUrl, isRatings).then(function (err, response) {
if (err) res.send(err);
res.send(response);
});
});
The data is never sent after resolving promise here at LINE 10: res.send(response); There's something wrong with inner promise but I am not sure not do I fix it?

It looks like you are mixing callbacks and Promises. I would recommend wrapping all calls to Promises.
From the looks of it you don't need inner Promise because request expects a callback so you can easily resolve the outer one.
You also have some branches of if statements that don't call resolve or reject (marked in the code bellow with comments). You have to call resolve/reject in all branches otherwise the Promise 'might do nothing' in case it hits the branch.
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) return reject(err);
parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
reject(new Error('Error in getting ratings'));
return;
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve(ItemLookup.Items[0].Item[0].CustomerReviews);
});
} else {
console.log('Resolved: ', ItemLookup.Items[0].Item);
resolve(ItemLookup.Items[0].Item);
}
} else {
// resolve or reject here
}
} else {
// resolve or reject here
}
});
});
});
}
Wrapping call to Promise:
function requestAsPromised(url) {
return new Promise(function (resolve, reject) {
request(url, function (err, response, body) {
if (err) {
reject(err);
} else {
resolve(body)
}
});
})
}

You need to use return to return the inner promise(s),
function getAmazonData(url, isRatings) {
var parsedData;
var ItemLookup;
var ratingsURL;
return new Promise(function (resolve, reject) {
return request(url, function (err, response, body) {
if (err) return reject(err);
return parseXML(body, function (err, parsedData) {
if (err) return reject(err);
ItemLookup = parsedData['ItemLookupResponse'];
if (ItemLookup && ItemLookup.Items && ItemLookup.Items.length > 0) {
// Request Error
if (ItemLookup.Items[0].Request && ItemLookup.Items[0].Request.length > 0 && ItemLookup.Items[0].Request[0].Errors) {
return reject(ItemLookup.Items[0].Request[0].Errors);
} else if (ItemLookup.Items[0].Item) {
ratingsURL = ItemLookup.Items[0].Item[0].CustomerReviews[0].IFrameURL[0];
if (isRatings) {
console.log('getting ratings...');
return new Promise(function (resolve2, reject2) {
request(ratingsURL, { json: true }, function (ratingsError, ratingBody) {
if (ratingsError) {
return reject2('Error in getting ratings');
}
ItemLookup.Items[0].Item[0].CustomerReviews.push({ Ratings: ratingBody });
resolve2(ItemLookup.Items[0].Item[0].CustomerReviews);
});
});
}
console.log('Resolved: ', ItemLookup.Items[0].Item);
return resolve(ItemLookup.Items[0].Item);
}
}
});
});
});
}
Since it looks like Node.js code, try using async/await

Related

I have been trying to fetch data from XMLRPC client

This is a piece of code I wrote. I have trouble accessing data from Promise for later purposes.
function forgotPassword(params) {
return new Promise(function (resolve, reject) {
return client.methodCall('forgotPassword', params, function (error, value) {
if (error === null) {
if (value === true) {
console.log('Password Sent!!');
//subpy.kill('SIGINT');
return resolve('Password Sent!!');
}
else {
console.log('User Not Found!!');
//subpy.kill('SIGINT');
return resolve('User Not Found!!');
}
}
else {
console.log('Error while doing the Operation!!');
//subpy.kill('SIGINT');
return reject(error);
}
});
}
);
}
I suggest you read the docs on Async Functions and Promises.
In this case you can do a few things
new Promise(function (resolve, reject) {
return client.methodCall('forgotPassword', params, function (error, value) {
if (error === null) {
if (value === true) {
console.log('Password Sent!!');
//subpy.kill('SIGINT');
return resolve('Password Sent!!');
}
else {
console.log('User Not Found!!');
//subpy.kill('SIGINT');
return resolve('User Not Found!!');
}
}
else {
console.log('Error while doing the Operation!!');
//subpy.kill('SIGINT');
return reject(error);
}
});
})
.then(res => console.log(res))
.catch(rej => console.log(rej));
The then will be called if the resolve is called.
The catch will be called if there is an error or reject is called.
Another way is to use await inside a async function to wait until you get a result from the promise object
function myPromiseFunction(){
return new Promise(function (resolve, reject) {.....
}
async function myfunction() {
try {
var res = await myPromiseFunction(); // waits until result from promise
console.log(res);
} catch (error){
console.log(error);
}
}
myfunction();

While call rest api in Javascript (NodeJS) and return response Undefined

When i call rest API and return response it show undefined but i console.log this response it return
var request = require("request");
function initialize() {
// Setting URL and headers for request
var options = {
url: 'http://postalpincode.in/api/pincode/400605',
json: true
};
// Return new promise
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.stringify(body));
}
})
})
}
function main() {
var initializePromise = initialize();
initializePromise.then(function (result) {
return result;
})
}
console.log('', main())
But when i console log this response it show output correct
var request = require("request");
function initialize() {
// Setting URL and headers for request
var options = {
url: 'http://postalpincode.in/api/pincode/400605',
json: true
};
// Return new promise
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.stringify(body));
}
})
})
}
function main() {
var initializePromise = initialize();
initializePromise.then(function (result) {
console.log('', result)
})
}
console.log('', main())
I want When i call rest API and return response it show correct output
The return inside the thenscope is not returning for the function main, but only for the thenscope of the promise. You need to return the promise like so:
function main() {
var initializePromise = initialize();
return initializePromise.then(function (result) {
return result;
})
}
main().then((result) => console.log('',result));
you can't make a sync function call an async method and expect to have get its result.
use async/await
async function main() {
var initializePromise = await initialize();
console.log(initializePromise)
}
My question is, why are you wrapping in a new Promise something that's already from a return type of Promise?
You could just do:
request.get(endpoint, options).then((response) => console.log(response)).catch((error) => console.log(error));
Let me know what's the output in that case.
The then resolution of initializePromise method resolves at a later stage when a response is fetched from REST HTTP call i.e. it does not get returned when you call main() method due to the fact it is async. To handle such code, you should either use a callback
function main(completionHandler) {
var initializePromise = initialize();
initializePromise.then(function (result) {
completionHandler(result);
})
}
main((result) => { console.log(result)})
or a promise
function main() {
// Return new promise
return new Promise(resolve => {
var initializePromise = initialize();
initializePromise.then(function (result) {
resolve(result);
})
}
}
main().then(result => console.log(result));
return new Promise(function (resolve, reject) {
// Do async job
request.get(options, function (err, resp, body) {
if (err) {
reject(err);
} else {
try {
resolve(JSON.stringify(body));
} catch(e) {
reject(e);
}
}
})
})
in main function:
function main() {
initialize().then((result) => {
console.log(result);
return result;
}).catch((err) => {
console.log(err);
return err;
})
}

asyncjs callback not being executed

I'm trying to merge the data from two JSON files (customer list) and then, create as many task as customers there are to be executed later via async.js
In order to have all the tasks array full, I'm controlling the end of the forEach with a promise:
var parsePromise = new Promise(function (resolve, reject) {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
originalData.forEach(function (element, index) {
var restoredCustomer = Object.assign(element, backupData[index]);
tasksArray.push((function (db, element) {
return function () {
db.collection('customers').insert(element, function (error) {
if (error) {
console.error("Error: ", error);
}
})
}
})(db, restoredCustomer));
forEachCounter--;
if (forEachCounter === 0) {
resolve(tasksArray);
}
});
});
});
Then, when the promise ends, I execute the async.js parallel method:
parsePromise.then(function (tasksArray) {
async.parallel(tasksArray, (err, results) => {
console.log("Operation completed successfully: ", results);
})
})
The weird thing is that the code is working fine, and the inserts are being done on MongoDB, however, the console.log("Operation completed successfully: ", results); placed in the parallel callback is never shown.
Try this
var parsePromise = new Promise(function (resolve, reject) {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
originalData.forEach(function (element, index) {
var restoredCustomer = Object.assign(element, backupData[index]);
tasksArray.push(function(cb){
db.collection('customers').insert(restoredCustomer, function (error) {
if (error) return cb(err);
cb();
})
});
forEachCounter--;
if (forEachCounter === 0) {
resolve(tasksArray);
}
});
});
});
According to async js documentation
https://caolan.github.io/async/global.html
tasks arguments in parallel function in async.js must be AsyncFunction.
AsyncFunction requires to have the final parameter which is a callback. The structure of the callback follows the standard of function(err, args).
Also you can try simplifying your code to:
new Promise((resolve, reject) => {
mongoDB.MongoClient.connect(dbURL, (error, db) => {
if (error) return reject(error);
resolve(db);
});
}).then(db => {
var tasksArray = [];
for (let i = 0; i != originalData.length; ++i){
var restoredCustomer = Object.assign(originalData[i], backupData[index]);
tasksArray.push(function(cb){
db.collection('customers').insert(restoredCustomer, function (error) {
if (error) return cb(err);
cb();
})
});
}
return new Promise((resolve, reject) => {
async.parallel(tasksArray, (err, results) => {
if (err) return reject(err);
resolve(results)
})
})
})

AWS Lambda/Node.js function call sequence

The following piece of AWS lambda code (highly simplified):
'use strict';
var aws = require('aws-sdk');
var docClient = new aws.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
var inputorder = initInputAndDbParams(event);
queryOrders(inputorder)
.then(findSuitableOrder.bind(null, inputorder)) //findOrder requires query result
.then(placeNewOrder(inputorder));
callback(null, "ORDER OK");
}
function queryOrders(inputorder) {
return new Promise(function(resolve, reject) {
docClient.query(inputorder.dbparams.qparams, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data); // gets back the query result array
}
}
}
// Recursive!!
function findSuitableOrder(inputorder) {
return new Promise(function(resolve, reject) {
if (array.length === 0 || orderamount === 0) resolve();
order = array.shift();
if (order.article = inputorder.article) {
deleteOrder(inputorder)
.then(placeNewOrder);
}
}
}
function deleteOrder(inputorder) {
return new Promise(function(resolve, reject) {
docClient.delete(inputorder.dbparams.delparams, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
}
}
function placeNewOrder(inputorder) {
return new Promise(function(resolve, reject) {
docClient.put(inputorder.dbparams.wparams, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
}
}
function placeOrderDelivery(inputorder) {
return new Promise(function(resolve, reject) {
docClient.put(inputorder.dbparams.odparams, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
}
}
function initInputAndDbParams(event) {
var inputorder = {
article: "example",
price: 54
amount: 1
};
return inputorder;
}
The expected Order of the function calls is:
initInputAndDbParams
queryOrders
findSuitableOrder
a) deleteOrder b) placeNewOrder
placeOrderDelivery
Actual result like:
initInputAndDbParams
placeOrderDelivery
queryOrders . .
I am using promises in all functions, except in initInputAndDbParams which returns a value. findSuitableOrder is called recursively to iterate through the array of orders.
How can I get the required sequence of function calls?
I got it:
queryOrders(inputorder).then(function (orders) {
return findSuitableOrder(inputorder, orders);
}).then(function() {
return placeNewOrder(inputorder);
}).catch(function (err) {
console.log(err);
That works an everything is in sequence.
Thanks for all your posts and help!

How to achieve sequential execution of code containing both testing and asynchronous function calls

I want to execute four blocks of code sequentially and conduct test during this sequential execution. The challenge is that the blocks contain calls of asynchronous functions.
I cannot seem to wrap my head around promises to use them properly and I just seem stuck after spending several hours on the block of code.
// Store current log of drone visits for DJI Phantom 4
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
console.log(err);
}
else {
current_drone_visits = rows[0]['COUNT(drone_id)'];
}
});
it("should return status 200 (OK) when requesting route for this function", function(done) {
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
assert.equal(res.status, 200);
done();
});
});
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
console.log(err);
}
else {
updated_drone_visits = rows[0]['COUNT(drone_id)'];
}
});
it("should increment the value of drones visited in the database", function(done) {
console.log("A - " + current_drone_visits);
console.log("B - " + updated_drone_visits);
assert.equal(current_drone_visits + 1, updated_drone_visits);
done();
});
What should I do here if I want to chain my callbacks such that they execute only after the previous function has finished?
Use async.js if you are comfortable with callbacks, or use Promises, you can chain them in order to synchronise you functions.
just wrap your callback in a function that return a promise like this
function P(){
return new Promise((resolve, reject)=>{
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) reject(err);
else {
resolve(rows[0]['COUNT(drone_id)'])
}
});
})
}
function T1(data){
//data in this case will be the result of the P() function
return new Promise((resolve, reject)=>{
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
if(err || res.status==200) reject(err);
resolve();
});
})
}
P()
.then(T1)
.then(...)
.catch((err)=>{
//handle error here
})
sry for the bad formatting
What should I do here if I want to chain my callbacks such that they
execute only after the previous function has finished.
This code does it for you:
describe ('test', function () {
this.timeout(6000);
it ('test', function(done) {
var query1 = new Promise (function (resolve, reject) {
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
reject(new Error('rejected query1'));
} else {
var current_drone_visits = rows[0]['COUNT(drone_id)'];
resolve(current_drone_visits);
}
});
})
.then(function(current_drone_visits){
var request1 = new Promise (function (resolve, reject) {
request.get("/product/DJI/Phantom_4").query({brand: dji_brand, model: dji_model}).end(function(err, res) {
if (err) {
reject(new Error('rejected request'));
} else {
resolve(res);
}
})
})
.then(function (res) {
try {
assert.equal(res.status, 200);
return current_drone_visits;
}
catch (err) {
done (err);
}
})
.catch(function(err) {
return Promise.reject(err);
})
return request;
})
.then(function(current_drone_visits) {
var query2 = new Promise (function (resolve, reject) {
db.query("SELECT COUNT(drone_id) FROM Drone_Visits WHERE drone_id = 2;", function(err, rows) {
if (err) {
reject(new Error('rejected query2'))
} else {
resolve();
}
})
})
.then(function () {
var updated_drone_visits = rows[0]['COUNT(drone_id)'];
var bundled = [current_drone_visits, updated_drone_visits];
return bundled;
})
.catch(function(err) {
return Promise.reject(err);
})
return query2;
})
.then(function(bundled) {
var current_drone_visits = bundled[0];
var updated_drone_visits = bundled[1];
console.log('A - ' + current_drone_visits);
console.log('B - ' + updated_drone_visits);
assert.equal(current_drone_visits + 1, updated_drone_visits);
done()
})
.catch (function (err) {
done(err);
})
})
})
You can use the this.timeout(6000) to determine how long Mocha will wait for your asynchronous code to complete. The default value is 2000 ms which may or may not be sufficient in your case.

Categories