Promisified API calls don't execute - javascript

I'm writing a service that makes an API call to get all bookings, then from those bookings gets a list all the coworkerIds who made those bookings. I then take the unique ids and use them to make another set of calls, to get an array of all the coworker records. Once this step is done I'll do the same for line item calls, however for some reason I can't get my list of coworker objects.
Here's my code:
apiService.getBookingsList(`?size=1000&from_Booking_FromTime=${weekAgo.toISOString()}`).then(bookings => {
const bookingCoworkerIds = bookings.map(booking => booking.CoworkerId);
const bookingCoworkerIdsUnique = bookingCoworkerIds.filter(recordParser.onlyUnique);
const getCoworker = function getCoworkerInfo(coworkerId) {
return apiService.getSingleCoworker(coworkerId);
}
const bookingCoworkerCalls = bookingCoworkerIdsUnique.map(coworkerId => getCoworker(coworkerId));
const bookingCoworkers = Promise.all(bookingCoworkerCalls);
bookingCoworkers.then(coworkers => {
console.log(coworkers.length);
console.log(coworkers[0]);
});
});
And here's the code for apiService.getSingleCoworker:
getSingleCoworker(coworkerId) {
// returns a single coworker object given their ID
return httpsRequest.createRequest(this.URL.coworkerList + `?Coworker_Id=${coworkerId}`, {}, this.requestHeaders, 'GET')
.then(result => JSON.parse(result).Records[0]);
}
I can also post my https code but I don't think that's the issue.
I suspect I'm doing something wrong with my promise pattern, as I'm still new to promises and async code in general. Neither of these console logs are reached, and instead the only output is:
(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): SyntaxError: Unexpected end of JSON input
So what am I doing wrong here?

The idea of working with promises is chaining "then" calls (or commands).
In order to do that, a promise must return another promise or a value.
Like this:
apiService.getBookingsList(`?size=1000&from_Booking_FromTime=${weekAgo.toISOString()}`)
.then(bookings => {
const bookingCoworkerIds = bookings.map(booking => booking.CoworkerId);
const bookingCoworkerIdsUnique = bookingCoworkerIds.filter(recordParser.onlyUnique);
const bookingPromises = bookingCoworkerIdsUnique.map(coworkerId => apiService.getSingleCoworker(coworkerId));
return Promise.all(bookingPromises);
}).then(coworkers => {
console.log(coworkers.length);
console.log(coworkers[0]);
}).catch(err => {
// do something with error
});
inside the first "then" callback I return Promise.all which generates a promise.
The next "then" receives all the resolved values by the "all" promise.
Bear in mind that promises should always contain a "catch" call at the end in order to capture promise errors.
EDIT:
getSingleCoworker(coworkerId) {
const self = this;
return new Promise(function(resolve, reject) {
httpsRequest.createRequest(self.URL.coworkerList + `?Coworker_Id=${coworkerId}`, {}, this.requestHeaders, 'GET')
.then(result => {
const jsonVal = JSON.parse(result).Records[0];
resolve(jsonVal);
}).catch(reject);
});
}
Hope this helps

Related

Access response object from json promise with fetch() in javascript [duplicate]

I do realise that when returning a non-promise in a .then() handler, it is immediately passed on to the next handler, if it's a promise that is returned, executing is halted for the promise to resolve before it is passed on to the next handler.
Also I know that only one value can be returned from a promise.
That beeing said, how would I go about returning multiple parameters from one .then() handler to the next one? Esepcailly if it's a mix of promises and non-promises. Currently I put everything into a custom object, return it, and use async await in the following then() handler for the promises to reslolve.
Then is use the resolved promise values and the non-promise value to do some work together.
This works fine but my gut is saying that this is somehow not the way it is supposed to be... maybe?
Example:
const current = 'blah';
const previous = 'blubb';
this.doSomeAsyncWork()
.then(
result => {
const nonPromiseValue = new domSomethingSynchronous(current, previous);
// "custom object, mix of promises and non-promises"
return {
nonPromise: nonPromise,
promiseA: ControllerA.asyncOperationA(current, nonPromiseValue.someProperty),
promiseB: ControllerB.asyncOperationB(nonPromiseValue.someOtherProperty),
}
}
)
.then(
async x => {
const nonPromiseValue = x.nonPromiseValue;
const valueA = await x.promiseA;
const valueB = await x.promiseB;
// do something with the results of those three variables
}
)
.catch(
// ...
)
Use return Promise.all on the array of Promises and non-Promises at the end of a .then, and then you can destructure the results immediately in the next .then, no await nor async needed.
The Promise.all will resolve once all Promises in the array have resolved. The non-Promises passed to it will just be passed to the next .then.
const makeProm = () => new Promise(resolve => setTimeout(resolve, 1000, 'resolveValue'));
Promise.resolve()
.then(() => {
const prom = makeProm();
const otherValue = 'foo';
return Promise.all([prom, otherValue]);
})
.then(([resolveValue, otherValue]) => {
console.log(resolveValue, otherValue);
});

Promise.all results are as expected, but individual items showing undefined

First of all, there are some issues with console.log in Google Chrome not functioning as expected. This is not the case as I am working in VSCode.
We begin with two async calls to the server.
promise_a = fetch(url)
promise_b = fetch(url)
Since fetch results are also promises, .json() will needed to be called on each item. The helper function process will be used, as suggested by a Stackoverflow user -- sorry lost the link.
let promiseResults = []
let process = prom => {
prom.then(data => {
promiseResults.push(data);
});
};
Promise.all is called. The resulting array is passed to .then where forEach calls process on item.json() each iteration and fulfilled promises are pushed to promiseResults.
Promise.all([promise_a, promise_b])
.then(responseArr => {
responseArr.forEach(item => {
process(item.json());
});
})
No argument is given to the final .then block because promiseResults are in the outer scope. console.log show confusing results.
.then(() => {
console.log(promiseResults); // correct results
console.log(promiseResults[0]); // undefined ?!?
})
Any help will be greatly appreciated.
If you are familiar with async/await syntax, I would suggest you not to use an external variable promiseResults, but return the results on the fly with this function:
async function getJsonResults(promisesArr) {
// Get fetch promises response
const results = await Promise.all(promisesArr);
// Get JSON from each response promise
const jsonResults = await Promise.all(results.map(r => r.json()));
return jsonResults
}
This is usage example:
promise_a = fetch(url1)
promise_b = fetch(url2)
getJsonResults([promise_a, promise_b])
.then(theResults => console.log('All results:', theResults))
Use theResults variable to extract necessary results.
You can try this, it looks the array loop is not going properly in the promise env.
Specifically: the promiseResults is filled after you are logging.
var resultAll = Promise.all([promise_a, promise_b])
.then(responseArr => {
return Promise.all(responseArr.map(item => return item.json()));
});
resultAll.then(promiseResults => {
console.log(promiseResults);
});

Issue with Nodejs promise

I have a sequence of function calls, connected with ES6 promises. Apparently, there is something wrong with this implementation, as API calls to the endpoint are not returning anything and the browser is stuck waiting for a response.
Please advise.
module.exports.insertTreatmentDetails = function (req, res) {
var doctorId = 10000
var departmentId = 10000
var procedureid = 10000
var hospitalSchema = new hospitalModel();
var p = new Promise(function (resolve, reject) {
counterSchema.getNext('Treatment.doctor.doctorId', collection, function (doctorId) {
doctorId = doctorId;
})
counterSchema.getNext('Treatment.departmentId', collection, function (departmentId) {
departmentId = departmentId
})
counterSchema.getNext('Treatment.procedureid', collection, function (procedureid) {
procedureid = procedureid
})
}).then(function () {
setData()
}).then(function (){
hospitalSchema.save(function (error, data) {
if (error) {
logger.error("Error while inserting record : - " + error)
return res.json({ "Message": error.message.split(":")[2].trim() });
}
else {
return res.json({ "Message": "Data got inserted successfully" });
}
});
});
};
The short answer is that you aren't calling resolve or reject inside the first promise in your chain. The promise remains in a pending state. Mozilla has a good basic explanation of promises.
How to Fix
It appears that you want to retrieve doctorId, departmentId, and procedureId before calling setData. You could try to wrap all three calls in one promise, checking whether all three have returned something in each callback, but the ideal is to have one promise per asynchronous task.
If it's feasible to alter counterSchema.getNext, you could have that function return a promise instead of accepting a callback. If not, I would recommend wrapping each call in its own promise. To keep most true to what your code currently looks like, that could look like this:
const doctorPromise = new Promise((resolve, reject) =>
counterSchema.getNext('Treatment.doctor.doctorId', collection, id => {
doctorId = id;
resolve();
}));
Then you could replace the first promise with a call to Promise.all:
var p = Promise.all([doctorPromise, departmentPromise, procedurePromise])
.then(setData)
.then(/* ... */);
Promises allow you to pass a value through to the next step, so if you wanted to get rid of your broadly-scoped variables (or set them in the same step where you call setData), you could just pass resolve as your callback to counterSchema.getNext and collect the values in the next step (also how you'd want to do it if you have counterSchema.getNext return a promise:
Promise.all([/* ... */])
.then(([doctorID, departmentID, procedureID]) => {
// If you aren't changing `setData`
doctorId = doctorID;
departmentId = departmentID;
procedureid = procedureID;
setData();
// If you are changing `setData`
setData(doctorID, departmentID, procedureID);
}).then(/* ... */).catch(/* I would recommend adding error handling */);

Function similar to Promise.some/any for an unknown amount of promises

I am creating a script in node.js (V8.1.3) which looks at similar JSON data from multiple API's and compares the values. To be more exact I am looking at different market prices of different stocks (actually cryptocurrencies).
Currently, I am using promise.all to wait for all responses from the respective APIs.
let fetchedJSON =
await Promise.all([getJSON(settings1), getJSON(settings2), getJSON(settings3) ... ]);
However, Promise.all throws an error if even just one promise rejects with an error. In the bluebird docos there is a function called Promise.some which is almost what I want. As I understand it takes an array of promises and resolves the two fastest promises to resolve, or otherwise (if less than 2 promises resolve) throws an error.
The problem with this is that firstly, I don't want the fastest two promises resolved to be what it returns, I want any successful promises to be returned, as long as there is more than 2. This seems to be what Promise.any does except with a min count of 1. (I require a minimum count of 2)
Secondly, I don't know how many Promises I will be awaiting on (In other words, I don't know how many API's I will be requesting data from). It may only be 2 or it may be 30. This depends on user input.
Currently writing this it seems to me there is probably just a way to have a promise.any with a count of 2 and that would be the easiest solution. Is this possible?
Btw, not sure if the title really summarizes the question. Please suggest an edit for the title :)
EDIT: Another way I may be writing the script is that the first two APIs to get loaded in start getting computed and pushed to the browser and then every next JSON that gets loaded and computed after it. This way I am not waiting for all Promises to be fulfilled before I start computing the data and passing results to the front end. Would this be possible with a function which also works for the other circumstances?
What I mean kind of looks like this:
Requesting JSON in parallel...
|-----JSON1------|
|---JSON-FAILS---| > catch error > do something with error. Doesn't effect next results.
|-------JSON2-------| > Meets minimum of 2 results > computes JSON > to browser.
|-------JSON3---------| > computes JSON > to browser.
How about thening all the promises so none fail, pass that to Promise.all, and filter the successful results in a final .then.
Something like this:
function some( promises, count = 1 ){
const wrapped = promises.map( promise => promise.then(value => ({ success: true, value }), () => ({ success: false })) );
return Promise.all( wrapped ).then(function(results){
const successful = results.filter(result => result.success);
if( successful.length < count )
throw new Error("Only " + successful.length + " resolved.")
return successful.map(result => result.value);
});
}
This might be somewhat clunky, considering you're asking to implement an anti-pattern, but you can force each promise to resolve:
async function fetchAllJSON(settingsArray) {
let fetchedJSON = await Promise.all(
settingsArray.map((settings) => {
// force rejected ajax to always resolve
return getJSON(settings).then((data) => {
// initial processing
return { success: true, data }
}).catch((error) => {
// error handling
return { success, false, error }
})
})
).then((unfilteredArray) => {
// only keep successful promises
return dataArray.filter(({ success }) => success)
})
// do the rest of your processing here
// with fetchedJSON containing array of data
}
You can use Promise.allSettled([]). the difference is that allSettled will return an array of objects after all the promises are settled regardless if successful or failed. then just find the successful o whatever you need.
let resArr = await Promise.allSettled(userNamesArr.map(user=>this.authenticateUserPassword(user,password)));
return resArr.find(e=>e.status!="rejected");
OR return resArr.find(e=>e.status=="fulfilled").
The other answers have the downside of having to wait for all the promises to resolve, whereas ideally .some would return as soon as any (N) promise(s) passes the predicate.
let anyNPromises = (promises, predicate = a => a, n = 1) => new Promise(async resolve => {
promises.forEach(async p => predicate(await p) && !--n && resolve(true));
await Promise.all(promises);
resolve(false);
});
let atLeast2NumbersGreaterThan5 = promises => anyNPromises(promises, a => a > 5, 2);
atLeast2NumbersGreaterThan5([
Promise.resolve(5),
Promise.resolve(3),
Promise.resolve(10),
Promise.resolve(11)]
).then(a => console.log('5, 3, 10, 11', a)); // true
atLeast2NumbersGreaterThan5([
Promise.resolve(5),
Promise.resolve(3),
Promise.resolve(10),
Promise.resolve(-43)]
).then(a => console.log('5, 3, 10, -43', a)); // false
atLeast2NumbersGreaterThan5([
Promise.resolve(5),
Promise.resolve(3),
new Promise(() => 'never resolved'),
Promise.resolve(10),
Promise.resolve(11)]
).then(a => console.log('5, 3, unresolved, 10, 11', a)); // true

Node JS : Array loop and function Not understanding how callbacks and promises work

New to concept of callbacks and promises. I'm trying to read contents of a directory which is correctly returning the addresses but this code is only printing console.log(value) and the console.log in the function getAccount "gettin info..." but nowhere printing the balance the api is getting closed and the process completes, I dont understand this because the address is being passed and the first console.log is printing inside the function but not doing further job. when i remove the fs.readdir and files.foreach and pass a single value to getAccount its working perfectly fine. No errors or bugs , its a runtime error and im guessing related to callbacks
'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});
function getAccount(address) {
var balance = 0.0;
console.log("getting info for :"+address);
return api.getAccountInfo(address).then(info => {
var balance = info.xrpBalance;
console.log("balance of "+address+" is "+balance);
return balance;
});
}
api.connect().then(() => {
fs.readdir(testFolder, function(err, files) {
// console.log("Total no of wallets : "+files.length);
// for (var i =3000, len=3200; i < len; i++) {
// var ad = files[i];
// console.log(ad + "\t" + typeof(ad));
// if(!xwallets.includes(ad))
files.forEach(function(value) {
getAccount(value).then(balance => {console.log(balance); });
console.log(value);
});
// console.log("running for index : "+i+" filedata :"+files[i]);
// }
});
// console.log(balance);
}).then(() => {
return api.disconnect();
}).then(() => {
console.log("done and disconnected");
}).catch(console.error);
You really don't want to mix asynchronous operations that return a promise with those that take a direct callback. It's hard to write good robust code when mixing like that. So, since you are already using with promises in a number of operations, it's best to take the remaining async operations and "promisify" them so you can use them with promises. Then, you can take advantage of all the clean error propagation and chaining of promises.
Then, secondly, you have to make sure all your asynchronous operations in a loop are properly chained to the parent promise. To do that here, we will accumulate the promises from getAccount() in the loop into an array of promises. Then, after the loop, we can tell when they're all done using Promise.all() and then return that promise from readdir() so that they will all be properly chained to the parent promise.
This will then make sure everything inside the loop finishes before the parent resolves and before you call api.disconnect().
Here's what that would look like:
'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});
function getAccount(address) {
var balance = 0.0;
console.log("getting info for :"+address);
return api.getAccountInfo(address).then(info => {
var balance = info.xrpBalance;
console.log("balance of "+address+" is "+balance);
return balance;
});
}
// promisify readdir
const readdir = util.promisify(fs.readdir);
api.connect().then(() => {
return readdir(testFolder).then(function(files) {
const promises = [];
files.forEach(function(file) {
console.log(file);
promises.push(getAccount(file).then(balance => {
console.log(balance);
// make balance be the resolved value for this promise
return balance;
}));
});
// make sure we are waiting for all these promises to be done
// by chaining them into the parent promise
return Promise.all(promises);
});
}).then(balances => {
// process balances array here
console.log(balances);
return api.disconnect();
}, err => {
// disconnect, even in the error case, but preserve the error
console.error(err);
return api.disconnect().then(() => {throw err;})
}).then(() => {
console.log("done and disconnected");
});
Summary of changes made:
Promisify fs.readdir() so we can use promise with it
Change how we call readdir() to use promises
Collect getAccount() promises inside of .forEach() into an array of promises
Add return Promise.all(promises) so that we wait for all those promises to be done and so that it is chained into the parent promise
Make balance be the resolved value of each promise in the .forEach() loop, even after logging its value
Make sure we're calling api.disconnect() even in the error cases
After logging error, preserve the error value as the reject reason

Categories