Clarification regarding promises and async/await needed - javascript

I have been using Promises and async/await, they are pretty much the same thing right? Usually what I would do is wrap my promise and return it etc.
function someFetchThatTakesTime(){
// Promisify the request.
return new Promise((resolve, reject) => {
if(allGood){
resolve();
}else{
reject();
});
}
Then I can do:
someFetchThatTakesTime()
.then(console.log('all good.')
.catch(console.log('some error occured.');
or I can do the:
async function wrapMyFetch() {
try {
// Make the async call.
data = await someFetchThatTakesTime();
return data;
} catch(err) {
// Propagate the exception up stream.
throw err;
}
}
(async () => {
let response = await wrapMyFetch();
// Do stuff with the response.
})();
I guess, pretty clear so far.
However, I have recently encountered situations where my application does not care about waiting for the results to be fetched and the data containers to be updated etc. Let's say there is one larger loop that runs infinitely and any Promised requests simply will fill-in the gaps as the application runs.
In that case, we don't really need the async/await pattern right? We just want to move forward with our loop and stones will fall in place behind us, whenever they are ready to fall in place (or not, in case of an error).
I would like to clarify this: async/await will just force things to run linearly, right? But if we do not want linearity, and we are ok with spawned tasks - Promises - to finish their thing in the background in some time when we are resuming with our run-cycle, we don't need that async/await pattern? Is that correct?

I have been using Promises and async/await, they are pretty much the same thing right?
Well, async/await are helpful syntax built on top of promises. Async/await relies on promises in order to work. So, I wouldn't quite call them the same thing, but you can code identically functioning code with await surrounded by try/catch and .then().catch(). You can use either. Both methods rely on promises.
However, I have recently encountered situations where my application does not care about waiting for the results to be fetched and the data containers to be updated etc. Let's say there is one larger loop that runs infinitely and any Promised requests simply will fill-in the gaps as the application runs.
In that case, we don't really need the async/await pattern right? We just want to move forward with our loop and stones will fall in place behind us, whenever they are ready to fall in place (or not, in case of an error).
There is no rule that the caller has to pay attention to a returned promise or has to wait for it before going on with their task. So, if you want to "just let the stones fall as they may in the background" as you say and that's appropriate for your application, you can code that way.
This is often referred to as "fire and forget" coding where you start some asynchronous operation that is going to do something on its own and you don't need to pay attention to when it finishes or if it had an error because that is of no consequence to the calling code.
But, there is a rule that you can't let a promise rejection go unhandled. So, if you're returning a promise and the caller isn't going to do anything with it, then you have to make sure that any rejections are handled by the operation itself (with .catch()) so that the only thing that ever gets returned back is a resolved promise. Even if all you do is notice the error and do nothing with it, you have to catch the error.

async/await will just force things to run linearly, right?
Not globally, no. await makes the code in the async function it's used in wait for the promise you pass it to settle, but that only affects the function you use it in, not anything calling that function.
async functions are syntactic sugar for writing a function that returns a promise. await is syntactic sugar for consuming promises. Together, they dramatically simplify using promises (in particular by making rejections errors that automatically propagate through the call tree).
More specifically, an async function runs its code synchronously until the first await or return (or until code runs off the end of the function), at which point it returns a promise. Its logic then waits for the promise to settle, and continues; eventually it settles its promise based on what happened to the last promise it awaited or returned (or fulfills it with undefined if code execution runs off the end).
If you don't want to wait for a promise to settle, you don't have to, not even in an async function. Just don't use await on it. For instance, assume we have this wrapper for fetch that gets JSON for us (and fixes the API footgun):
async function fetchJSON(...args) {
const response = await fetch(...args);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
}
Here's an async function using it that does an initial query to get a list of things to fetch, and then fetches all of those things in parallel::
async function fetchList(listUrl) {
const list = await fetchJSON(listUrl);
return Promise.all(list.map(item => fetchJSON(itemUrl)));
}
Notice how fetchList uses await to wait for the list of things to fetch, but then doesn't wait for the items; it just returns the promise from Promise.all for the list of items it starts fetching.
Also note how await within fetchJSON makes fetchJSON's logic wait for the fetch promise to settle, but doesn't make the caller calling fetchJSON wait unless that caller uses await. fetchList only awaits the first call (to get the list), it doesn't wait for the item from the list.

Related

Promise.allSettled rewrite in async await

I got a piece of code that would submit several set of reports which they are independent each other, currently wrote in promise.allSettled, but I was told that the team standard would require async await instead of promise
"Never use multiple await for two or more independent async parallel tasks, because you will not be able to handle errors correctly. Always use Promise.all() for this use case."
1
"In comparison, the Promise returned by Promise.all() may be more appropriate if the tasks are dependent on each other / if you'd like to immediately reject upon any of them rejecting."
2
"Using for await...of, you have more granular control of the promises. So if the order in which promises complete is important to you, for await...of is your preferred choice. However, the increased control isn’t free. The fact that for await...of handles promises one by one, makes it a lot slower."
"To sum up, the three methods are all capable of handling iterables of promises, but differ slightly in their functioning. Use for await of if the order in which promises are resolved is important to you. Use Promise.all() if the order isn’t important and you need all calls to succeed. Use Promise.allSettled() if the order isn’t important and you don’t absolutely need all individual calls to be successful."
3
After some research, I found it is not possible to rewrite it in async await with the same efficiency (request execute in parallel) and simplicity (promise.allSettled is a built-in function), am I correct?
That piece of code
const recordInsertErrors:Object[] = [];
await Promise.allSettled(
jsonArray.map((eachPositionReport) => {
return PositionReport.query().insert(eachPositionReport).catch((err) => {
const error = { vessel_ownership_id: eachPositionReport.vessel_ownership_id, error: err.nativeError };
recordInsertErrors.push(error);
throw err;
});
}),
);
First of all JavaScript code does not run in parallel. The most we can say is that it executes asynchronously, i.e. it gets executed by the engine while monitoring its job queues. The "only" thing that might execute in parallel is lower-level, non-JavaScript logic, such as provided by some APIs that make asynchronous HTTP requests.
Secondly, whether an asynchronous operation starts while another is still underway, is not determined by the use of Promise.all, Promise.allSettled, for await ... of, ...etc, but by whether or not all involved promises are created immediately or not. That is part of the code that is not orchestrated by any of the mentioned constructs.
So surely you can use async and await keywords to achieve that asynchronous requests are made without waiting that a previous one has completed.
For instance:
const recordInsertErrors = [];
const promises = jsonArray.map(async (eachPositionReport) => {
let value;
try {
value = await PositionReport.query().insert(eachPositionReport);
} catch(err) {
value = {
vessel_ownership_id: eachPositionReport.vessel_ownership_id,
error: err.nativeError
};
}
return value;
});
// All promises will now fulfill, as errors are converted to
// fulfillments with an error property
(async () => {
for (const promise of promises) {
const value = await promise;
if (value.error) recordInsertErrors.push(value);
console.log(value);
}
})();
The for loop with await expressions will not delay the moment at which all promises have resolved. It will potentially report sooner on some results than Promise.allSettled, as the latter is designed to first wait until all promises have settled, and only then resolve its own promise.

Why should we wrap await inside an async function?

Why should we have an async function in order to use await? Why can't we just use await without async? JS is async by default too, this just adds to the confusion.
Update:
I've seen some lads put my question on hold so I'll try to elaborate.
I'm just curious as to why this won't work:
some code
let users = await getUsers();
some code
Why should it be inside an async for it to work, ie
$(async function() {
some code
let users = await getUsers();
some code
});
JS is async by default too...
No, JavaScript is not async by default. The only async features of JavaScript are fairly newly-added:
Promise resolution
async/await
JavaScript is commonly used in environments where you interact with asynchronous things (like event handlers in the DOM, or I/O completions in Node.js), but JavaScript is not asynchronous (other than above).
In the words of Allen Wirfs-Brock, who was the editor of the ECMAScript specification for many years, JavaScript...
(has) an observably synchronous execution model. Other than via Atomics/SABs there are no observable shared-state race conditions.
Back to your question:
Why should we have an async function in order to use await?
Before too long, with modules you won't have to, once the top level await proposal finishes working through the process. It just got to Stage 3.
But the answer is that await is syntactic sugar for consuming a promise, and one of the rules of promises is that you either handle errors or return the chain to the caller (so it can handle errors or return the chain to its caller). await doesn't handle errors, so it has to return the chain to the caller. The way it does that is that an async function always returns a promise, and that promise is chained to the promise await awaits.
That is, this:
async function foo() {
const thingy = await somethingAsyncReturningAPromise();
return thingy.foo;
}
is conceptually (but not literally) this:
function foo() {
return somethingAsyncReturningAPromise()
.then(thingy => thingy.foo);
}
If something goes wrong in somethingAsyncReturningAPromise, the promise returned by foo rejects — the error is propagated to the caller.
As far as I can tell from the top-level await proposal, it simply allows unhandled rejections at the top level of the module to be unhandled rejections. So just like this code causes an unhandled error:
null.doSomething();
this code in an async module would cause an unhandled rejection:
await somethingThatReturnsAPromiseAndRejects();
Why should we have an async function in order to use await? Why can't we just use await without async?
Because async/await is "just" syntactic sugar for Promises. If the function is async, then it returns a Promise. It is not possible to have the "await" behaviour without returning a promise. The fact that the function is async has to be marked explicitly.
JS is async by default too, this just adds to the confusion.
This statement is too "simplified". While it is true that JS is async in nature, because of the event loop, this doesn't mean that every function has an async behavior. This does not add to the confusion. You're probably confused due to misunderstanding how JS really works. You should read about Promises, which are behind the scenes when you see async/await.
JavaScript has task based concurrency. It basically means that code blocks (tasks) runs synchronously without being interrupted, until it reaches a certain breakpoint (the end of a task). That has some advantages:
1) You do have concurrency, e.g. a network call does not block your script from doing other things in the meantime (so the task that consumes the network request will only run if the network request was done, other tasks can be done in the meantime).
2) On the other hand, you do not have concurrent mutations, which eliminates a lot of problems (let a = 1; a += 1; could evaluate to 3, you would need locks / semaphores to prevent those, c.f. Java & others).
Now async / await allow you to define such tasks:
An async function can be divided into tasks, await serves as a breakpoint:
let a = 1;
async function stuff() {
a = a + 1; // this is totally secure, as no other code might run in the meantime
a = a + await other(); // this is unsafe, as we await, which means that other tasks might be executed in the meantime.
}
If you want to "await in non async functions" that basically means that you won't know wether a certain function call runs synchronously (meaning: without other code running in the meantime) or not:
function dangerous() { await stuff(); }
let a = 1;
a = a + dangerous(); // does that work or not? Can a be manipulated in the meantime?
So with your proposal you would basically remove the borders around tasks, every code might run every inbetween. So at the end that causes chaos, and chaos is not good if you want to be productive.

Promises.all() breaking sequential chaining

In the past I've used the jQuery deffered/promises, but I'm trying to move to javascript Promises. I have a function that needs to make several network calls in sequence and then several that can happen in any order, but all need to resolve before moving forward. I start with the sequential calls and they go in order, but then with the results of the second call I need to iterate over those results and make a variable number (at the moment 6) of further calls. I don't care what order those are done in, but do need to wait for all of them to resolve before I proceed. I thought this pattern would work. But it doesn't.
function doLotsOfStuff(){
firstNetworkCall()
.then(function(data){
// do stuff with data
return secondNetworkCall();
})
.then(function(data){
// do stuff with data
var promises = data.map(function(item){
// All of these calls (happens to be 6)
// need to be done before I continue
return thirdIndependentCall(item.Info);
});
// at this point I see [Promise, Promise, ....]
// all are unresolved
return Promise.all(promises);
})
.then(function(results){
// executes immediately after the Promises.all() line
// none of them are resolved
// results is just one unresolved promise
});
}
I can chain the final step onto the Promises.all() like this
return Promise.all(promises)
.then(function(results){
// this works!
})
But if I want to chain more things after that I have to keep stepping them in. Seems like I'm missing some piece of information.
EDIT
I had copied over a typo in my simplified code which isn't in my actual code. The suggestion to add a .catch() in my chain was a good one and I tried it. Now it throws firstNetworkCall(...).then(...).then(...).catch is not a function.
I think the problem is that some of my code (somewhere) is still relying on jQuery.Deffered and that doesn't play well with Promise.
It is good to know that my initial pattern should work -- if I'm consistent with what kind of async handling I'm working with.
Change this:
Promises.all(promises)
to this:
Promise.all(promises)
What you have is throwing an exception (because Promises is not defined which is caught by the .then() and then rejects the promise chain.
This is a classic reason why you should always have a .catch() at the end of your chain because if you were logging that rejection, it would have probably told you what the error was.
If you want to convert any jQuery promises to ES6 promises so you can use .catch() anywhere or to just guarantee consistent behavior, you can wrap them with Promise.resolve() as in:
Promise.resolve(firstNetworkCall()).then(...).catch(...)
or this:
var promises = data.map(function(item) {
// All of these calls (happens to be 6)
// need to be done before I continue
return Promise.resolve(thirdIndependentCall(item.Info));
});
// at this point I see [Promise, Promise, ....]
// all are unresolved
return Promise.all(promises);
And, keep in mind that some newer jQuery versions are more compatible with ES6 promises than others.

Why Use JS Promises Instead of IF/ELSE / Ternary?

I'm doing some reading up on JS Promises to up-skill.
Here's my quandry:
Say you want to console.log('we done, bruh!') AFTER your data's come back.
so with a Promise, you might say:
let iWantToLogOut = function() {
let data = fetch('https://jsonplaceholder.typicode.com/users')
return new Promise((resolve) => {
resolve(data)
})
}
And then resolve that promise like:
iWantToLogOut().then((dataBack)
=> databack.json())
.then((json) => {
console.log('We done, bruh! Look: ', json)
})
So that's great. You get your API data back and then we log our msg out.
But isn't it just way easier to go:
let data = fetch('https://jsonplaceholder.typicode.com/users');
data ? console.log('we done, bruh!') : null;
I'm probably over-simplifying/missing something (because... well... i'm retarded) but I just want to make sure i'm really understanding Promises first before i move onto Async/Await.
But isn't it just way easier to go:
let data = fetch('https://jsonplaceholder.typicode.com/users');
data ? console.log('we done, bruh!') : null;
It would be, but it doesn't work. What fetch returns is a promise, not the result of the operation. You can't return the result of an asynchronous process. More: How do I return the response from an asynchronous call?
In the upcoming ES2017 spec, though, we have syntactic sugar around promise consumption which will let you write this:
let data = await fetch('https://jsonplaceholder.typicode.com/users');
// --------^^^^^
console.log('we done, bruh!');
Note we don't even need the conditional, because await converts a promise rejection into an exception.
That code would need to be in an async function, e.g.:
(async function() {
let data = await fetch(/*...*/);
// use data here
})();
The JavaScript engines in some browsers already support async/await, but to use it in the wild, you'll want to transpile with Babel or similar.
Note: You've shown
so with a Promise, you might say:
let iWantToLogOut = function() {
let data = fetch('https://jsonplaceholder.typicode.com/users')
return new Promise((resolve) => {
resolve(data)
})
}
There are a couple of problems with that code:
It never settles the promise you created if the fetch fails.
It calls something data which is not data, it's a promise of data (that's mostly style, but it's misleading).
It exhibits the promise creation anti-pattern. You already have a promise (from fetch), no need to create another.
iWantToLogOut should be simply:
let iWantToLogOut = function() {
return fetch('https://jsonplaceholder.typicode.com/users');
};
That returns a promise that will be resolved with the data, or of course rejected. Which you'd then consume with promise methods or await (within an async function).
It is not a matter of easy.
Usually network calls should be handle asynchronously(I don't want to the anti-pattern of synchronous AJAX calls). At that point you have few options to handle it:
Callbacks
Promises
Observables
In you code above, when it's synchronous, the fetch should return immediately with a promise that will be resolve to the data only when the server has responded. Only then you can check the data for it's content. Further. Because every promise can be fulfilled or failed, in your then you can have a handler for each instead of using the ternary.
From the latest spec:
Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user’s experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when current global object is a Window object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an InvalidAccessError exception when it occurs.

Understanding the sequence of operations when using ES6 Promises

Take a look at this sample JavaScript code that uses Promises:
const promiseGen = () => {
return new Promise((resolve, reject) => {
reject('something')
})
}
promiseGen().catch(err => console.log(err))
console.log('after promiseGen')
I would have expected the output to be:
something
after promiseGen
But instead, it is
after promiseGen
something
Can someone explain why this is happening? Since I'm creating the promise first, which in turn calls reject, shouldn't something be printed first? Is the promise run on a separate thread such that the order of operations is not guaranteed?
The Promise is always async (even if ou resolve it immediately), and will therefore always resolve after the sync code.
The specification of Promises require that even if they are doing nothing async they should behave in async manner for uniformity. So here, the then/catch method of the promise will get called on the next event loop check, which will happen when the call stack gets empty, which will become empty only after the execution of the last console.log() statement. Hence the results.

Categories