How to optimize this block of code since its async - javascript

var orderItems = userData.shoppingcart;
var totalPrice = 0;
userData.shoppingcart.forEach(function(itemName, i){
_data.read('menuitems', itemName, function(err, itemData){
if(!err && itemData)
{
totalPrice += itemData.price;
if(++i == userData.shoppingcart.length){
// Only here when all the itemNames have been read I should continue
}
}
});
});
As you can see, the call to _data.read is async, because I am reading from a file.
But I need to wait to have all the files read, so I can have the totalPrice calculated. That's why I place that condition [ ++i == userData.shoppingcart.length ].
I am new to javascript and nodejs in general, never was very good programmar at it, but anyway my point is that I am sure this isn't a good approach, what if both files are readed at same time, and then that condition is never ran or the calculation of totalPrice is badly done?
Can someone give me please some guidance on this?
Thank you in advance!

Given that you don't specify what context this is in, I'm going to make a few assumptions:
I assume that _data.read() doesn't already support returning a promise.
I assume that this code needs to either call a callback function or return a promise.
My (somewhat naive) approach to this would be to:
Map the orderItems into Promises for each price of that item.
Map the result into a total
Return that resulting promise OR call the callback.
Here's an annotated example of how you might do it:
// Promise.all takes an array of promises and returns
// a new promise that completes when all the promises in the array are complete.
const promiseOfPrices = Promise.all(
// Here we map all the items in the shopping cart into promises for each of their prices
userData.shoppingcart.map(
// The Promise object takes a single function that it will immediatly call with functions to resolve or
// reject the promise. I suggest reading up on Promises if you're not familiar with them.
itemName => new Promise((resolve, reject) => {
// Here, we have a `reject` and `resolve` function that will each complete the new promise,
// either in success or error respectfully.
// Do the actual read of your file or database or whatever
_data.read('menuitems', itemName, (err, itemData) => {
// If there was an error, reject this promise.
if (err) reject(err);
// Otherwise, we're successful and we resolve with the price of the item
else resolve(itemData.price);
});
})
)
);
// Now, we have a promise (promiseOfPrices) for all the prices of the items in the cart. We use `then` which will
// perform a transform on the result, much like the `map` function on an Array.
const promiseOfTotal = promiseOfPrices.then(
// Here we use the `Array.reduce` function to succinctly sum the values in the array.
arrayOfCartItemPrices => arrayOfCartItemPrices.reduce(
// For each item, reduce calls our function with the current sum and an item in the array. We produce a new
// sum by adding the sum to the item price.
(sum, itemPrice) => sum + itemPrice,
// This is the initial value for sum, 0.
0
)
);
If you can return a promise, and you just want to return the total, then
return promiseOfTotal;
If you have a callback that expects (err, result), then do something like this:
promiseOfTotal.then(
result => callback(null, result),
error => callback(error, null),
)
If you need to do more work on the result, you can do so with another then:
promiseOfTotal.then(
priceSum => {
// Do work here
},
// Optionally handle errors here:
error => {
// Do error handling here.
}
)
Note that by using promises, arrow functions, and array comprehensions (map and reduce) we avoid complex and hard to follow mutation of variables and loops. This is a "functional" style of programming, and while somewhat harder to learn, is generally safer and cleaner than the alternatives. I suggest taking the time to understand how this works, as it'll help you write code that's less likely to have bugs when you're dealing with complex things like asynchronicity.
Finally, I haven't run this code. It may well have a bug or two. Feel free to ask for clarification or if it doesn't work.
Best of luck!
P.S. I didn't go in to using async/await because I think it would be less clear than using the Promises directly, and the use of Promise.all is needed for the parallelism anyways. It's absolutely possible to use them to good effect here, but I'll leave that as an exercise to the OP.

This is how you can read the items in sequence using promises (async/await flavour):
var orderItems = userData.shoppingcart;
let totalPrice = 0;
for (let itemName of userData.shoppingcart) {
const itemData = await _data.read('menuitems', itemName);
totalPrice += itemData.price;
}
This example assumes that _data.read supports async/await. If it doesn't, however, it can "promisified" using the promisify function in nodejs' util module

Related

Promise Chain Within a for-each Loop Inside a .then() of Another Promise

In JavaScript, I'm calling a promise which returns an id; we'll call this promise1. promise1 has a following .then() which, given the id returned from promise1, calls a for-each loop. On each iteration of this loop, promise2 is carried out
promsie2 also has a .then(). so the data returned from promise2 can be utilized.
mycode.js
exports.my_function = (req, res) => {
var data = req.body;
var name_array = ["Jeff", "Sophie", "Kristen"]
var personal_ID_arr = []
promise1.getID(data) //promise1 gets an int 'id'
.then(id => {
array.forEach(name => { //for each name
promise2.getPersonalID(name, id) //promise2 creates a personal id for each person using their name and the id generated from promise1
.then(personalID => {
personal_ID_arr.push(personalID) //add the generated personal id to an arr
})
})
})
//will need to operate on 'personal_ID_arr' here after the above finishes or possibly still within the promise1's .then; as long as the arr is filled fully
}
However, I'm running into the issue of synchronicity not allowing for this type of logic to happen, as things begin happening out of order once it gets to the loop aspect of the program. I need to carry out another function with the filled-out 'personal_ID_arr' after these promises are carried out as well
I've looked at other questions regarding promise chaining but this is a different case due to the fact the for loop needs to work with the data from the first promise and use .then() inside a .then(). Surely, I don't need to keep this structure though if a better one exists.
Any help would be greatly appreciated
Keeping an array of IDs that an asynchronous operation will fill, is not something that will help you carrying out more asynchronous operations on the array later. Instead, consider using an array of Promises for each name, and wait them all to resolve using Promise.all. Also, i removed the personal_ID_arr from the function my_function variables and moved it to the appropriate asynchronous operation. I did a simple console log of the array, you should fill your next step there.
Although maybe you need to write this logic with Promises, but I would suggest using async / await for this task, it will lead to more readable code.
exports.my_function = (req, res) => {
var data = req.body;
var name_array = ["Jeff", "Sophie", "Kristen"]
promise1.getID(data) //promise1 gets an int 'id'
.then(id =>
//map the array of Strings to array of Promises instead, wait for them to resolve
Promise.all(name_array.map(name =>
promise2.getPersonalID(name, id)
))
)
.then(personal_ID_arr => console.log(personal_ID_arr))
//will need to operate on 'personal_ID_arr' here after the above finishes or possibly still within the promise1's .then; as long as the arr is filled fully
}

Resolve Promise After Executing Single Line Of Code

I have a sequence of events I'm trying to align, but I don't understand how to resolve a promise after a single line of asynchronous code. In this case, I'm making an API call which resolves to an array of objects. I'm then using Object.assign() to convert the array of objects into an object of objects. Once completed, I then want to call a function which does something with that new object of objects. The conversion process takes some time, and I'm not sure how to postpone my function call until after the object has been converted. My non-functioning code is given below.
this.queryCodes().then(() => {
new Promise((resolve) => {
resolve(() => {
// This code isn't executing.
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
});
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
resolve should be given the result of your asynchronous operation, not the code to execute. The code you want to execute shouldn't go in the resolve argument, but instead in the callback you give to the promise constructor. So for your snippet:
this.queryCodes().then(() => {
return new Promise((resolve) => {
// Put the code you want to execute here...
// Call resolve when your work is done, and pass it the result if there is one.
resolve(/* maybe result here */);
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
It's also worth noting that you don't need to use a promise constructor since you're already chaining off a promise to begin with. You could just:
this.queryCodes().then(() => {
// Do additional work here...
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
return this.resolveCodesToDescriptions();
});
resolve() doesn't take a callback as an argument. It takes the resolved value as an argument. Not sure where you got the callback notion from. That's why the callback is never called because that's not a supported feature of resolve().
Manually creating a promise inside a .then() handler is likely not needed and is probably an anti-pattern. You don't show enough of the real code here to understand what is going on, but this is likely way more complicated than it needs to be. From the code you show, I think you can just do this:
this.queryCodes().then(() => {
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
this.resolveCodesToDescriptions();
});

How to execute function for each observable in RXJS

I have a situation with observable like this:
//pseudo code
var agreement:Observable<agreement[]> = Observable.ajax({...})
.flatMap(agreements:[] => {
agreements.forEach(agreement =>{
//server request for each agreement
//If request fail throw exception
});
return agreements;
})
.map(agreements => agreements);
Here, I need to check each agreement if it`s exist in other server and throw exception if not. Main question is how wait while all all agreements are checked adn only then get result.
The answer to this depends on what's happening inside the forEach loop – if agreements (the one going into the flatMap) is an Observable that you're using forEach to emit, the strategy is a bit different altogether because you don't necessarily know how many agreement objects will be emitted.
Assuming though the forEach is just running some other basic http calling, say, one that returns promises... The answer is then going to involve collecting the promises and allowing them all to complete the Promise-way, so that the completion can then be converted into an Observable for the next operator in the chain.
Some pseudo code:
var agreement: Observable<agreement[]> = Observable.ajax({...})
.flatMap(agreements:[] => {
const promises = agreements.map(agreement => doHttpThing()
.then(...)
.catch(err => Observable.throw(err))
);
return Observable.fromPromise(Promise.all(promises));
})
.map(agreements => agreements);

How to synchronously call multiple times any function which is returning promise

For example, I have function which Promise.resolve() if I already have any cached entity id else it make ajax call to reserve entity id and then Promise.resolve() new entity id
function getReservedEntityId(collectionName) {
//if (!haveCachedIds) {
//make ajax call to reserve new ids
Promise.resolve(newId);
}
return Promise.resolve(cachedId);
};
How can we synchronously call it multiple times to reserve multiple entity ids?
P.S. I know that the correct approach is to make this function take parameter that will specify the count of entity ids and make request and return ids accordingly but I wanted to understand how can we call synchronously multiple times any function which is returning promise.
First, the implementation of getReservedEntityId() needs to make correct use of promises. I recommend a thorough reading of how promises work. In particular, it's important to understand that when your function performs an asynchronous task, you need to return a promise that will either resolve or reject based on the result of the asynchronous task.
function getReservedEntityId(collectionName) {
if (haveCachedIds) {
return Promise.resolve(cachedId);
} else {
return new Promise((resolve, reject) => {
// Make the AJAX call and call resolve(newId) in the success callback
// or call reject(errCode) in the failure callback.
// The arguments newId and errCode can be any values you like and are
// the values that get passed to the next link in the promise chain
// i.e. the values passed to then() or catch()
});
}
}
With that squared away, there are two recommended ways to make the calls synchronous:
1) Utilize a promise chain
getReservedEntityId(collectionName)
.then((id) => {
// Probably want to do something with `id` first...
return getReservedEntityId(collectionName);
})
.then( ... )
.then( ... );
Of course, if you're going to pass the same function to each .then() call, you could just as well declare it as a regular function so as to not repeat yourself.
2) Using async/await
This is a new ES2017 feature and is still not widely supported. As of the time of this writing, Node.js supports async/await with the --harmony flag, but most browsers do not. That said, async/await is intended for this exact purpose, treating functions returning promises as though they were synchronous. If you want to start using async/await in your code now, it is common practice to use JavaScript transpilers which which transpile your future-ready JavaScript to code that is supported by all major browsers.
This is how you would use async/await:
(async function someAsyncFunction {
const id1 = await getReservedEntityId(collectionName);
const id2 = await getReservedEntityId(collectionName);
const id3 = await getReservedEntityId(collectionName);
.
.
.
})();
The syntax is much nicer and more readable than the promise chain because it's designed for this exact purpose. Note that I have made the function self-invoking here so that it matches your behavior without having to make an extra function call. But you can use and call a function defined with async function just like any other function that returns a promise.
#fvgs your answer is also correct. But here's the complete solution and the challenge which I have faced is to maintain list of reserveIds which was in response of every getReservedEntityId call.
getEntityIds: function (entity, count) {
if (!count || count == 1)
return Utils.getReservedEntityId(entity);
var promise = new Promise(function(resolve, reject) {
var result = [];
var chain = Utils.getReservedEntityId(entity);
var callBack = function CB (result, newId) {
result.push(newId);
if (result.length == count)
resolve(result);
else
return Utils.getReservedEntityId(entity);
}.bind(null, result);
for (var i=1; i <= count; i++) {
chain.then(callBack);
}
chain.catch(reject);
});
return promise;
}

Chaining Promises recursively

I'm working on a simple Windows 8 app in which I need to fetch a set of data from a web site. I am using WinJS.xhr() to retrieve this data, which returns a Promise. I then pass a callback into this Promise's .then() method, which supplies my callback with the returned value from the asynchronous call. The .then() method returns another Promise, giving it the value that my callback returns. The basic structure of such a query would be as follows:
WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
//do stuff
return some_value;
}).then(
function secondcallback( some_value )
{
//do stuff
});
In my situation, however, I may need to make additional queries for data depending on the data returned by the first query, and possibly more queries depending on THAT data... and so on, recursively.
I need a way to code this such that the final .then() is not executed until ALL the recursions have completed, similar to this:
function recurse() {
return WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
if( result_from_xhr == something )
{
recurse();
}
});
}
recurse().then(
function final()
{
//finishing code
});
The problem is that, of course, the finishing code is called as soon as the first level of recursion completes. I need some way to nest the new promise and the old promise from within the callback.
I hope my question is clear enough, I'm really not sure how to explain it and frankly the idea of asynchronous recursive code makes my head hurt.
What I would do here is to create a whole new, standalone promise that you can complete manually, and return that from the recurse() function. Then, when you hit the point that you know you're done doing async work, complete that promise.
Promise.join works when you've got a known set of promises - you need the entire array of promises available before you call join. If I followed the original question, you have a variable number of promises, with more possibly popping up as part of async work. Join isn't the right tool in these circumstances.
So, what does this look like? Something like this:
function doSomethingAsync() {
return new WinJS.Promise(function (resolve, reject) {
function recurse() {
WinJS.xhr({ url: "http://www.example.com/" })
.then(function onResult(result_from_xhr) {
if (result_from_xhr === something) {
recurse();
} else {
// Done with processing, trigger the final promise
resolve(whateverValue);
},
function onError(err) {
// Fail everything if one of the requests fails, may not be
// the right thing depending on your requirements
reject(err);
});
}
// Kick off the async work
recurse();
});
}
doSomethingAsync().then(
function final()
{
//finishing code
});
I rearranged where the recursion is happening so that we aren't recreating a new promise every time, thus the nested recurse() function instead of having it at the outer level.
I solved this problem in perhaps a different way (I think it's the same problem) while making my own Windows 8 app.
The reason I came across this problem is because, by default, a query to a table will paginate, and only return 50 results at a time, so I created a pattern to get the first 50, and then the next 50, etc, until a response comes back with less than 50 results, and then resolve the promise.
This code isn't going to be real code, just for illustration:
function getAllRows() {
return new WinJS.Promise(function(resolve, reject){
var rows = [];
var recursivelyGetRows = function(skipRows) {
table.skip(skipRows).read()
.then(function(results){
rows = rows.concat(results);
if (results.length < 50) {
resolve(rows);
} else {
recursivelyGetRows(skipRows + 50);
}
})
}
recursivelyGetRows(0);
});
}
so getAllRows() returns a promise that resolves only after we get a result back with less than 50 results (which indicates it's the last page).
Depending on your use case, you'll probably want to throw an error handler in there too.
In case it's unclear, 'table' is a Mobile Services table.
You will need to use Promise.join().done() pattern. Pass an array of Promises to join(), which in your case would be a collection of xhr calls. The join() will only call done() when all of the xhr Promises have completed. You will get an array of results passed to the done(), which you can then iterate over and start again with a new Promise.join().done() call. The thing to be away of, when using this approach, is that if one of the Promises passed to join() fail, the entire operation is treated as an error condition.
Sorry I don't have time right now to try and stub out the code for you. If I get a chance, I will try to later. But you should be able to insert this into your recursive function and get things to work.
Well, I solved my problem; my recursive function was misinterpreting the data and thus never stopped recursing. Thank you for your help, and I'll be sure to watch those screencasts, as I still don't quite fully grasp the Promise chaining structure.

Categories