Chaining multiple .then, some functions without asynchronous code. Best practise? - javascript

I have one function that runs asynchronously, such as querying records from a database. The remaining code depends on the result, but runs synchronously. I am struggling to understand a good way to deal with this and would like to know the best practise?
Imagine the following code:
var usersDataPromise = getUsersDataFromDB();
var usersAgeArray = usersDataPromise.then(extractAges);
var averageAge = calculateAverageAge(usersAgeArray);
here, getUsersDataFromDB() runs synchronously and returns a promise. When the promise resolves, it runs extractAges(), a function that simply takes some of that data and returns an array. This function is sync. I want to pass that on to calculateAverageAge(), but this is where it breaks: calculateAverageAge does not wait for getUsersFromDB() to finish, and simply starts as soon as it can (usersAgeArray will be undefined).
What I tried:
I can make extractAges() return a promise instead of an array, and make calculateAverageAge wait with .then, by doing:
var usersDataPromise = getUsersDataFromDB();
var usersAgeArrayPromise = usersDataPromise.then(extractAges);
var averageAge = usersAgeArrayPromise.then(calculateAverageAge);
However, it feels as if I made the code needlessly complicated: I made extractAges() return a promise even though it runs asynchronously, not synchronously.
I realize I could have called calculateAverageAge() at the end of the extractAges function, but I'm trying to learn how to keep the code clean, and this feels as if I'm introducing side effects to a function that should only be extracting an array. Now the function description seems not fully accurate: it extracts an array but also calls on the next function.
I hope I've made this clear, I'm having trouble wrapping my head around it. What is the best way to deal with this? Thanks!

I can make extractAges() return a promise instead of an array
There's no reason to do that. Only because you are using a function in a then callback, it doesn't need to return promises.
and make calculateAverageAge wait with .then
Yes, you need to use then anyway, because the array is produced asynchronously and usersDataPromise.then(extractAges) always returns a promise. There's no alternative.
var averageAgePromise = getUsersDataFromDB().then(extractAges).then(calculateAverageAge);

Related

Is it legit to use .map() method only for side-effects?

Is it legit (or good practice) do a loop with a higher order function - like Array.map() - to perform some side effects ?
This is mostly a theoretical question since I notice that sometimes I found (or perform myself) some loops using the .map() method, like this:
let myObject = {...}
let myItemsArray = [ ... , ... ]
myItemsArray.map( (item, index) => {
// do some side effect for ex:
myObject.item[index] = item
}
But I know that this map() method actually returns an array. So calling myItemsArray.map() is like I'm returning an array without assign it to any variable.
So the question, is this legit? Should I avoid this and use a classic for() loop or a .forEach() method?
Side question: one of the reasons I'm asking this is because I need to perform some loops on an async function so promises and await operators are involved, I remember that forEach() loops are not ideal on an async function, but wanted to know why.
So the question, is this legit? Should I avoid this and use a classic for() loop or a .forEach() method?
If you aren't returning anything from the map function, then you should use forEach instead. You end up with the same result but you don't imply, to anyone maintaining your code, that you are returning something useful.
one of the reasons I'm asking this is because I need to perform some loops on an async function so promises and await operators are involved, I remember that forEach() loops are not ideal on an async function, but wanted to know why.
Neither forEach nor map will await if the function you pass to it returns a promise (which async functions do).
So there are three possible scenarios here:
// Your loop
...myItemsArray...
// Code that comes after the loop
...etc...
A: The items in the loop need to handled sequentially
Use a regular for () loop as the outer function won't pause.
B: The items in the loop can be handled in parallel and the code that comes after doesn't need to wait for it
Use a forEach.
C: The code that comes after needs to wait for everything in the loop to finish
Use a map. Pass the array of returned promises to Promise.all. Then await that.

How to push a function that returns a promise to an array, which accepts params

So I have a bunch of mysql calls I'm making that I'm looking to make the code a little cleaner and more re-usable by separating the logic out into their own functions and what I want to do, since the number of calls can always grow, is build a promise array and execute them via Promise.all. I'm already familiar with this concept, but usually my approach would be something like this:
const promiseChain = []
array.forEach(item => promiseChain.push(new Promise((resolve, reject) { ... logic });
Or something similar to this. But I can't quit do it this way because I want to seperate out all the logic, so now I have a bunch of different functions, all returning promises. I'm a little confused on what syntax I can use to now just add the functions to the array without actually calling them. I did see one SO answer which specified something like this:
const array = [func1, func2]
Although I don't think this works, bc my functions would all have different parameters. Is there a good, clean way to add functions that return promises to an array (obviously without executing them) that also receive parameters? Or would it just be something like this:
promiseChain.push(new Promise((resolve, reject) => {
functionPromise(params).then(() => resolve()).catch(err => reject(err))
})
I guess what I'm asking is, is there a better way to do it then the above? Just seems like more code than necessary since those functions already return promises. Seems like overkill.
is there a better way to do it then the above?
Your example is equivelent to:
promiseChain.push(functionPromise(params));
Is there a good, clean way to add functions that return promises to an array (obviously without executing them) that also receive parameters
To add functions without executing them:
array.push(functionPromise);
The param can be bound to the functions in your array:
array.push(funcitonPromise.bind(funcitonPromise, ...params))
Then they can be invoked later by:
await Promise.all(array.map(func => func()));

What's wrong with awaiting a promise chain?

I’m working on an Angular 6 application and I’ve been told the following is an anti-pattern:
await someFunction().then(result => {
console.log(result);
});
I realize that it is pointless to await a promise chain. If someFunction() returns a promise, you don’t need a promise chain if you’re awaiting it. You can do this:
const result = await someFunction();
console.log(result);
But I’m being told awaiting a promise chain can cause bugs, or that it will break things in my code. If the first code snippet above does the same thing as the second snippet, what does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?
I’m being told awaiting a promise chain will break things in my code.
Not necessarily, your two code snippets do indeed work the same (as long as someFunction() really returns a promise).
What does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?
It's harder to understand and maintain, it's confusing to mix different styles. Confusion leads to bugs.
Consider that you would need to add another promise call at the location of the console.log() call, or even a conditional return from the function. Can you use await in the callback like elsewhere in the function, do you need to return the result from the then callback, is it even possible to return from the outer function? All these questions don't even come up in the first snippet. And while they can be easily answered for your toy example, it might not be as easy in real code with more complex and nested control flow.
So you should prefer the more concise and clean one. Stick to await for consistency, avoid then in async functions1.
1: Of course, there's always an exception to the rule. I would say that it's cleaner to use promise chaining for error handling in some cases where you would use catch or the second then callback.
Under the hood, async/await is just promises.
That is, when you have some code that looks like:
const result = await myAsyncFunction();
console.log(result):
That's exactly the same as writing:
myAsyncFunction().then(data => {
const result = data;
console.log(result);
});
The reason then - that you shouldn't mix async/await and .then chains - is because it's confusing.
It's better to just pick one style, and stick to it.
And while you're picking one - you might as well pick async/await - it's more understandable.
If your then code returned a promise instead of calling console.log, your first example would await, but your second example would not.
When you use async/await, you will catch your rejects in try/catch blocks. Your code will be less nested and clearer.
Using then often results in more nesting, and harder to read code.
You can await anything, whether or not it returns a promise. Sometimes this future-proofs calling a method that may one day become async or just return a promise without declaring async.
The downsides are complexity, performance, and compatibility, all of which pale in comparison to the gains.
I find that if you rely on a function's return value after calling it, and it is or may eventually become asynchronous, decorate calling your functions with await to your heart's delight, whether or not it is current async or returns a promise.

Return an object with a "then" function within "Promise.then()"

Within my Node.JS application I have written a function (findByReference) that goes to a database and asynchronously yields a fetched database row. I have written this function using Promises. Additionally, I have written an implementation of the Maybe monad and want my findByReference function to yield an instance of Maybe.
My code looks like the below:
findByReference(r)
.then(raw => raw ? Just(raw) : Nothing())
.then(row => {
(row instanceof Maybe) === true;
});
Without going into what Just and Nothing mean, the implication of this (because of how I've written Maybe) is that the row variable in the above code has a function on it called "then". To cut a long story short, it appears that Javascript is getting confused and is for some reason automatically calling MY "then" and instead of passing the Maybe is actually passing to the callback whatever MY "then" returns as the value of row. This is obviously leading to all manner of weird behaviour. If I simply remove the "then" function from my object then it all works as expected.
I am aware that if a Promise.then returns another Promise, then execution will pause until that promise is resolved. I have been unable to find any official documentation to back this up, but is it the case that this decision is simply based on the existence of a "then" function (the closest I have found is this https://developers.google.com/web/fundamentals/primers/promises which refers to the return value as "something Promise-like"). If this is the case, it would be my understanding that "then" as a function name is basically a reserved word in Javascript? I have seen other implementations of Maybe (such as this one https://www.npmjs.com/package/data.maybe) that use the word "chain" for a similar thing - I wondered if this is why?
Can anyone shed any light on if my deduction here is correct and if so is there any workaround I can use other than renaming my function?
FYI the only other SO question I've found that touches this problem is this one - Resolve promise with an object with a "then" function - but since that is angular-specific I don't believe this is a duplication.
Thanks in advance!
...the row variable in the above code has a function on it called "then". To cut a long story short, it appears that Javascript is getting confused and is for some reason automatically calling MY "then"...
It's not confused. :-) This is the definition of how promises work. JavaScript's promises work according to the Promises/A+ specification, which uses this terminology:
1.1 “promise” is an object or function with a then method whose behavior conforms to this specification.
1.2 “thenable” is an object or function that defines a then method.
If you have an object passing through a promise chain that's a thenable but not a promise, it's incompatible with promises.
So yes, in a sense, the then property of objects passing through promise chains is "reserved" by the Promises/A+ spec. You'll need to wrap your raw value in an object that doesn't have a then (and then unwrap it later). Or if you can, rename then in your design to remove the conflict.

javascript node.js in a stand-alone script, block/wait on a promise

I have a simple program in node.js, such as:
// CODE1
step1();
step2();
var o = step3();
step4(o);
step5();
step6();
this program is meant to be run in a stand-alone script (not in a web browser),
and it is a sequential program where order of execution is important (eg, step6 needs to be executed after step5).
the problem is that step3() is an async function, and the value 'o' is actually passed to a given callback,
so I would need to modify the program as follows:
// CODE2
step1();
step2();
step3( function (o) {
step4(o);
step5();
step6();
})
it could make sense to call step4 from the callback function, because it depends on the 'o' value computed by step3.
But step5 and step6 functions do not depend on 'o', and I have to include them in that callback function only to preserve the order of execution: step3, then step4, then step5 then step6.
this sounds really bad to me.
this is a sequential program, and so I would like to convert step3 to a sync function.
how to do this?
I am looking for something like this (eg using Q):
// CODE3
step1();
step2();
var deferred = Q.defer();
step3(deferred.resolve);
deferred.blockUntilFulfilled() // this function does not exist; this is what i am looking for
var o = deferred.inspect().value
step4(o);
step5();
step6();
How to do this?
ps: there are advantages and disadvantages of using sync or async, and this is a very interesting discussion. however, it is not the purpose of this question. in this question, i am asking how can i block/wait until a promise (or equivalent) gets fulfilled. Please, please, please, do not start a discussion on whether sync/blocking is good or not.
It's impossible to turn an async operation into a sync operation in vanilla JavaScript. There are things like node-fibers (a C++ add-on) that will allow this, or various compile-to-JS languages that will make the async operations look sync (essentially by rewriting your first code block to the second), but it is not possible to block until an async operation completes.
One way to see this is to note that the JavaScript event loop is always "run to completion," meaning that if you have a series of statements, they will always be guaranteed to execute one after the other, with nothing in between. Thus there is no way for an outside piece of information to come in and tell the code to stop blocking. Say you tried to make it work like so:
stepA();
stepB();
while (global.operationIsStillGoing) {
// do nothing
}
stepC();
This will never work, because due to the run-to-completion nature of JavaScript, it is not possible for anything to update global.operationIsStillGoing during the while loop, since that series of statements has not yet run to completion.
Of course, if someone writes a C++ addon that modifies the language, they can get around this. But that's not really JavaScript any more, at least in the commonly understood sense of ECMAScript + the event loop architecture.

Categories

Resources