Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at the best features of ES2017.
Async Functions and Array.prototype.forEach()
Array.prototype.forEach
doesn’t work with the async
and await
syntax.
For instance, if we have:
async function downloadContent(urls) {
urls.forEach(async url => {
const content = await makeRequest(url);
console.log(content);
});
}
then we won’t get all the results of the promises because forEach
doesn’t wait for each promise to finish.
Instead, we want to use the for-of loop to iterate through each async function to get our result:
async function downloadContent(urls) {
for (const url of urls) {
const content = await makeRequest(url);
console.log(content);
}
}
The for-of loop is aware of the await
operator so we can use it loop run all the async functions sequentially.
If we want to run the async functions in parallel, we can use Promise.all
:
async function downloadContent(urls) {
await Promise.all(urls.map(
async url => {
const content = await makeRequest(url);
console.log(content);
}));
}
We mapped the URLs to async functions so that we can call Promise.all
on the array of promises.
And we return a promise with the resolved value being an array of the promises’ resolved values.
Immediately Invoked Async Function Expressions
We can create async functions that are run immediately.
For instance, instead of writing:
async function foo() {
console.log(await promiseFunc());
}
foo();
We can write:
(async function () {
console.log(await promiseFunc());
})();
It can also be an arrow function:
(async () => {
console.log(await promiseFunc());
})();
Unhandled Rejections
We don’t have to worry about unhandled rejections when we use async functions.
This is because browsers report them to us when we encounter them.
For instance, we can write:
async function foo() {
throw new Error('error');
}
foo();
Then we’ll see the error logged in the console.
Shared Array Buffers
ES2017 introduced shared array buffers which lets us build concurrent apps.
They let us share the bytes of a SharedArrayBuffer
object between multiple workers and the main thread.
The buffer us shared and is wrapped in a typed array so we can access them.
We can share data between workers quickly and coordination between workers is simple and fast.
For instance, we can create a shared array buffer by writing:
const worker = new Worker('worker.js');
const sharedBuffer = new SharedArrayBuffer(
100 * Int32Array.BYTES_PER_ELEMENT);
worker.postMessage({
sharedBuffer
});
const sharedArray = new Int32Array(sharedBuffer);
We created a worker in worker.js
.
Then we created a shared buffer with the SharedArrayBuffer
.
It can contain 100 elements.
Then to share the buffer with the worker, we call postMessage
to pass the buffer to the worker,.
To access the buffer’s data, we create a new Int32Array
instance.
Then in the worker.js
worker, we get the buffer by writing:
self.addEventListener('message', (event) => {
const {
sharedBuffer
} = event.data;
const sharedArray = new Int32Array(sharedBuffer);
//...
});
We listen to the message
event and get the sharedBuffer
property of event.data
.
Then we can access it the same way.
Conclusion
Async functions don’t work well with existing array instance methods.
Also, we can use shared array buffers to share data between the main and worker threads.