The word to note here is feel
JavaScript is great. It’s extremely useful and I enjoy building apps with it. Other people I’ve spoken to don’t seem to share this feeling. When I’ve questioned them on why they don’t like JavaScript, it’s usually because they feel it goes against how asynchronicity should work
I’ll try and explain how JavaScript achieves asynchronicity and show you how you can get JavaScript to feel synchronous like you are used too
JavaScript achieves asynchronicity on a single thread (that is, at-least from a JavaScript developers points of view, you don’t need to create threads in order to have multiple tasks running in parallel)
The concept of an event loop is very interesting and how you deal with the nuances of achieving asynchronicity in JavaScript is to understand another three concepts: callbacks, promises and async/await
What single-threaded means is your JavaScript code is running in a single-thread with only one call stack and only a single function can be read off the stack and processed at a time. Any Web API that is synchronous will not involve the callback queue. Rather, the function will be placed directly on the call stack for current processing. This is very bad idea because no other processing can occur when the call stack is waiting for something that is slow or considered blocking (the network is the most likely candidate for being slow as is file manipulation etc)
When you invoke an asynchronous Web API (which there are many of), it kicks off the function (the browser will handle the multi-threading) and will push the callback onto the callback queue when the asynchronous Web API is complete and has a result. The callback queue will contain the callback function with the result/message as a parameter
The event loop, which can also be seen as a while loop, is constantly reading from the callback queue and waiting (synchronously that is–the event loop will ask the front of the queue for the callback and wait until it’s provided to it) to move the callback from the callback queue and push it into the call stack for current processing
There are much deeper explanations on this subject matter all over the web but I feel this is a basic idea and should aim you in the right direction
Sometimes you would like JavaScript to feel more synchronous, you’d just like to wait for the called function that contains the asynchronous logic to return a result without continuing the rest of the code. Like I said, using synchronous language constructs (‘while’ as an example) or synchronous Web APIs (XHR used to be able to do synchronous requests but that was deprecated ages ago) are a definite no-no. So, how do you approach this?
Async/await are the language constructs you are wanting to use
Here is the JavaScript code I’ve written to demonstrate this concept:
/* * The calling function needs to be predicated with the 'async' language construct * and this is the function that will feel asynchronous to the outside caller */ window.onload = async function() { function waitForMe() { /* * A Promise is basically a placeholder for what's to come */ return new Promise(function(resolve, reject) { /* * The browser exposes many asynchronous Web APIs and setTimeout() * is just an example of one of them */ setTimeout(function() { resolve('OK, done'); }, 5000); }); } console.log('Getting ready'); /* * The 'await' language construct will automatically reference the result of * invoking resolve() when the Promise completes * * This will appear to be synchronous/blocking (the result will be waited for) */ console.log(await waitForMe()); console.log('Lets go'); }
Async/await works hand-in-hand with promises. Promises are basically a placeholder for what’s to come in future. In my code example above, setTimeout() will invoke resolve() when it has completed and before it’s completed, the promise is not fulfilled
Contrast the above feeling with the following code:
console.log('Getting ready'); waitForMe().then(function(resolve) { console.log(resolve); }).catch(function(reject) {}); console.log('Lets go');
Notice the difference? When you invoke waitForMe(), the rest of the code will continue running and this is the asynchronous feeling you are probably used to when working with JavaScript
Callback functions are basically what you want to have invoked when the asynchronous process is complete. Often, they are passed in as parameters to the asynchronous Web API
Ensure you are dealing with promises if you would like to work with async/await and wrap your asynchronous processing inside the promise and invoke the first parameter of the executor/callback, that you pass into the constructor of the promise, to indicate success or invoke the second parameter to indicate failure
Yes, JavaScript does have primitive types but even these primitive types behave like objects. Examples: a string has .toUpperCase(), a number has .floor()
I don’t see anything wrong with this concept. It’s just the nature of the language
I haven’t used many JavaScript frameworks where NPM is most commonly used. All my JavaScript work is vanilla JavaScript I wrote directly for the browser and not transpiled in some way
I have been involved in large JavaScript projects (mostly worked on the deployment pipelines and configured an architecture for hosting Node) where the node_modules folder is well over 600Mb and I would not like to be involved in debugging issues here. Imagine trying to debug the type error or uncaught exception occurring in someone else's library on line 64753 in src/core/superComplicated.ts
Yeah, I agree and NPM is probably above me
JavaScript does not follow classical inheritance, rather its prototypical. It has the concept of ‘upwards’ traversal and if you are not careful, this traversing will occur all the way to the global scope. ES5 does introduce the concept of ‘use strict’ that solves this problem
JavaScript also has issues with what exactly ‘this’ refers too. In other languages, ‘this’ always refers to the current object but in JavaScript the current object can be deceiving (particularly in cases where you use objects in callbacks). There are mechanisms to cater for this (haha) though like implicit/explicit binding but it usually means just being careful
The inheritance model is different, not bad