Hi everyone. I’m on the fullstack engineering career path. After almost 3 months of hard study and planning my portfolio, I’ve finally come across the front-end section in which it is explained how async functions work in Javascript.
I had to search some more YouTube videos with further details and examples and I still have some doubts.
So Javascript works with the event loop, a process that decides which objects from the event queue goes in for execution—that grants the ability to act as is it was a multithreaded language. This is part of the core of the language, and it works like that; no matter what runtime you are in (browser, Node, etc.).
Until now, it’s pretty clear that there is a do... while -kind-of-orchestrator in the backstage: the event loop.
The first question I have is why do we need an event loop handling that, instead of a do...while loopity loop?
And the answer comes along pretty quick: you would have to wait for an entire loop to finish before “going back” into the do...while portion of the code that contains the evaluation. Not very smart.
So promises, right? We have promises. An async function will generate an object called promise that will hold two references (I think I should say attributes here) we are interested in:
The status (i.e., pending, resolve, reject)
The value
Ok, until now it’s pretty clear.
Now I have one question:
Why do we need promises or async functions when we have something like event listeners? Are event listeners some kind of promise?
When we need to call an external API, I’m being taught that we need to use the fetch() native method on JS. Does this mean that we need to always do it via promises? Why can’t we call an external API with a simple synchronous function? e.g., const syncFunction(URL){() => methodOrSomethingThatWouldGETRequests(URL)}
Are promises necessary? Was I introduced to sync functions just so I understand easily, but in reality, async functions are used everywhere?
I think I understand the concepts and all, but I’m having issues when trying to understand why do we call an external API with fetch and that’s the rule we’ve been introduced to. I have the feeling that event handlers on web browsers would have a similar behavior to async functions, because it doesn’t matter when you click, it will always register—and not only when you are coincidentally passing through that part of the code. So event handlers are async too? I’m kinda confused.
Event listeners are neither promises nor async functions. They are functions or procedures we add to events that respond to that event occurring. Event listeners can involve promises but do not have to.
fetch() returns a promise object. You could call an external API with a synchronous function, but then the rest of your code would have to wait for the API to respond before it could continue. Async functions allow us to do other things while we wait for something to be done.
Promises are necessary aspects of async functions. Async functions are indeed used everywhere because the internet is largely dependent on asynchronously retrieving information while other elements of a page are rendered and other functions are run.
Hello! Thanks for replying. Regarding event listeners: if they aren’t async functions, how can they be “listening” all the time without stopping the rest of the code from executing? Because you declare an event listener to a button, and if you press it on the browser (no matter when or how many times), the event listener will act upon it. Are they sent to the event queue, do they remain in the callstack? Where are they?! LOL. Thanks for your response. I understand now that using async functions is an improvement for retrieving tasks.
That’s a common misconception: event listeners are not always listening. A signal is emitted (or “fired”) by the browser in response to an event occurring, such as a user’s “click”. When we attach an event listener (or event handler) to the event, it is called when the event is produced. We’re attaching it, so the event is already being listened to, in a sense, by the browser which produces the signal in response. When a user clicks, then, the signal is produced and the event is registered as having occurred. The interpreter, in this case a browser, checks to see if an event listener has been attached to the event object. If there is, then it runs whatever code is associated with that event listener/handler. So, despite its’ name, the event listener is not actually “listening” - that’s just an easy abstraction.
Browser creates event object with data on event target and other built-in properties (i.e. the click occurred on a certain button).
If an event listener was created on the appropriate event target with the event that just occurred as its’ event type, then it runs the event listener code (i.e. document.getElementById('certain-button').addEventListener('click', someFunction())).
Thank you for the explanations! Last question if you’re free now to reply:
I’ve seen throughout the course that whenever you run async functions, most of them are imported from modules. So for example:
app.js
import { sum } from './supportFn.js';
const toSum = sum;
toSum(5, 7); //we invoke the imported function with 2 integers, 5 and 7
supportFn.js
export async sum(firstInteger, secondInteger) {
console.log("I'm not actually going to add these numbers, instead I will call another function to do so");
return trulySum(firstInteger, secondInteger);
}
const trulySum = (first, second) => {
return first+second;
}
This code would import sum, an async function that resides in another file (supportFn.js). So as you can see, sum is not actually adding up numbers, it’s just calling an “executor” function named trulySum.
trulySum is not exported. How is it possible that toSum works anyway in app.js? Is this because sum is async? Or… imported functions would ACTUALLY execute in their corresponding files? Why would trulySum work if it’s not exported?
In my mind, this is creating confusion and I think that with someone telling me how this works, it will alleviate the road ahead. Thanks in advance! I’m marking your first response as Solution today
trulySum() does not need to be defined within app.js because it’s defined within sum(), and all of sum() is imported.
I’ve seen throughout the course that whenever you run async functions, most of them are imported from modules.
This is just what you’ve been seeing, but the two aren’t connected. In real-world programming, functions may be defined in other files, sync or async, and this is mostly for code organization and efficiency. The hardest and most time-consuming part of programming is often reading code. If you can abstract away the implementation details of a given function and just leave someone with a well-named function that does what you think it would, then they can more easily reason about your code and work with it.
Like if I was making a complicated fetching function in React that provided you with the response, an isLoading flag, and a error, if it arises, I could create a generic such function and just give you the function and say, now you can use this on any webpage to fetch the info you’re looking for in a safe and useful way.
But you don’t really need to know exactly how it works. All you need to know, really - unless you’re bugfixing that function in particular - is what that function returns. So I say, this function, fetchData() returns an object with three elements of data: the response object, an isLoading flag, and an error object.
End note: this is essentially what libraries are - collections of code that do common things in robust ways that you can import into your apps to get things done easier and faster.