Reader small image

You're reading from  JavaScript Design Patterns

Product typeBook
Published inMar 2024
Reading LevelIntermediate
PublisherPackt
ISBN-139781804612279
Edition1st Edition
Languages
Right arrow
Author (1)
Hugo Di Francesco
Hugo Di Francesco
author image
Hugo Di Francesco

Hugo Di Francesco is a software engineer who has worked extensively with JavaScript. He holds a MEng degree in mathematical computation from University College London (UCL). He has used JavaScript across the stack to create scalable and performant platforms at companies such as Canon and Elsevier and in industries such as print on demand and mindfulness. He is currently tackling problems in the travel industry at Eurostar with Node.js, TypeScript, React, and Kubernetes while running the eponymous Code with Hugo website. Outside of work, he is an international fencer, in the pursuit of which he trains and competes across the globe.
Read more about Hugo Di Francesco

Right arrow

Asynchronous Programming Performance Patterns

A key strength of JavaScript runtimes is the event loop, which couples “non-blocking input/output” within a single-threaded execution model. This means JavaScript is great for high-concurrency systems as long as they are not compute-bound systems (i.e., they’re IO-bound).

With the asynchronous and non-blocking IO, JavaScript has strong built-ins to orchestrate requests. In this chapter, we’ll cover the following topics:

  • Sequential and parallel asynchronous operation patterns in JavaScript, both with Promises only and with async/await
  • The cancellation and timeout of fetch requests with AbortController
  • Advanced asynchronous operation patterns: throttling, debouncing, and batching

At the end of this chapter, you’ll be able to spot and remedy situations where the asynchronous operation orchestration could be improved in JavaScript.

Technical requirements

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Javascript-Design-Patterns

Controlling sequential asynchronous operations with async/await and Promises

Promises were introduced in ES2015 (ES6), along with other modern data structures.

For those familiar with JavaScript prior to ES2015, asynchronous behavior was modeled with callback-based interfaces, for example, request(url, (error, response) => { /* do work with response */ }). The key issues that Promises resolved were the chaining of asynchronous requests and issues around managing parallel requests, which we’ll cover in this section.

ES2016 included the initial specification for the async/await syntax. It built on top of the Promise object in order to write asynchronous code that didn’t involve “Promise chains,” where different Promises are processed using the Promise().then function. Promise functionality and async/await interoperate nicely. In fact, calling an async function returns a Promise.

We’ll start by showing how to use Promises to manage sequential...

Parallel asynchronous operation patterns

A common source of bad performance is running operations sequentially that could be completed in parallel.

For example, a naive implementation of loading a cart and then the contained products would be as follows:

Figure 7.2: Load cart then each of the three products contained from fakestoreapi

Figure 7.2: Load cart then each of the three products contained from fakestoreapi

In this case, the operation completion time is composed of the sum of the following:

  • Request-response time for GET /carts/{cartId}
  • Request-response time for GET /products/1
  • Request-response time for GET /products/2
  • Request-response time for GET /products/3

There is a requirement for the /products/{productId} calls to be done after the GET /carts/{cartId} call completes since that’s where the product IDs are coming from. What isn’t required is for each product call to wait for the previous one to complete; the calls only depend on data from the GET /carts/{cartId} call. This is an...

Asynchronous cancellation and timeouts with AbortController

Another source of bad performance in applications in general is doing work that’s not necessary. In the context of a JavaScript web application, one of the types of “work” that can be unnecessary (and therefore a drain on performance) is having HTTP requests that aren’t required any more. For example, in a photo gallery system or any paginated system, when moving across photos, the request for the previous photo might not have completed before the next one is started. In this case, the previous request data is not necessary any more, as we’re essentially on a completely different page.

In these instances, cancelling the request might be useful.

AbortController is a Web/DOM API that allows us to abort web requests. It’s created using its constructor, new AbortController, and controlling a request (to potentially cancel it) is done with the AbortController().signal value, which...

Throttling, debouncing, and batching asynchronous operations

Throttling is an operation in which requests are dropped until a certain time is reached. For example, for a 10 ms throttle timeout, once a request is made, no request in the next 10 ms will be sent. If multiple requests are made between 0 ms and 10 ms, only the last request will be sent after the 10 ms timeout expires.

In JavaScript, such a throttle function can be implemented as follows.

A higher-order function, throttle takes in an fn parameter and returns an executable function with the same input signature as the fn parameter.

When the “throttled” fn function is called, we set isThrottled = true in order to be able to discard calls between the first call and a configured timeout:

function throttle(fn, timeout) {
  let isThrottled = false;
  return (...args) => {
    isThrottled = true;
    return fn(...args);
  };
}

We...

Summary

In this chapter, we’ve covered asynchronous operation orchestration patterns with Promises and async/await to manage sequential and parallel operations. We also covered advanced patterns such as request cancellation, implementing timeouts, the difference between throttling and debouncing, and finally, how to use batching in an asynchronous operation context.

In order to manage sequential asynchronous operations, we can use a Promise-based approach with Promise().then(), async/await, or mix both approaches. This helps keep our code simple to reason about. For parallel execution, we can leverage Promise.all() with Promise.then() or async/await. We also have multiple approaches to maintaining response data across asynchronous operations.

We can leverage AbortController to cancel requests. We implemented a timeout for the fetch response time using AbortController and setTimeout. Stopping in-flight requests is a useful cleanup step that can improve performance by reducing...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
JavaScript Design Patterns
Published in: Mar 2024Publisher: PacktISBN-13: 9781804612279
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Hugo Di Francesco

Hugo Di Francesco is a software engineer who has worked extensively with JavaScript. He holds a MEng degree in mathematical computation from University College London (UCL). He has used JavaScript across the stack to create scalable and performant platforms at companies such as Canon and Elsevier and in industries such as print on demand and mindfulness. He is currently tackling problems in the travel industry at Eurostar with Node.js, TypeScript, React, and Kubernetes while running the eponymous Code with Hugo website. Outside of work, he is an international fencer, in the pursuit of which he trains and competes across the globe.
Read more about Hugo Di Francesco