Concurrency

#folder

Definition

Concurrency is the ability of different parts or units of a program to be executed out-of-order or in a particular order without affecting the outcome. This allows for parallel execution of the concurrent units.

Atomic Operations

Definition

An atomic operation is one where no other concurrent unit may see the operation in a partially complete state.

We want to make sure all updates in code are atomic, else we might get race conditions.

Example

An example from ESLint:

let totalLength = 0;

const addLengthOfSinglePage = async (pageNum) => {
  totalLength += await getPageLength(pageNum)
}

Promise.all([addLengthOfSinglePage(1), addLengthOfSinglePage(2)]).then(() => {
  console.log('The combined length of both pages is', totalLength)
})

This code looks like it will sum the results of getPageLength(1) and getPageLength(2), but in reality this creates a race condition which is undesirable.

Let's assume getPageLength(1) completes first. Then it will be added to totallength. But when getPageLength(2) completes, it's going to add to totalLength with the initial value of totalLength. This is a problem because the value of totalLength will be equal to getPageLength(2) instead of the desired getPageLength(1) + getPageLength(2).

One solution is to read totalLength at the same time it's updated:

const addLengthOfSinglePage = async (pageNum) => {
  const lengthOfThisPage = await getPageLength(pageNum)

  totalLength += lengthOfThisPage
}

Another solution is to avoid using a mutable reference at all:

Promise.all([getPageLength(1), getPageLength(2)]).then(pageLengths => {
  const totalLength = pageLengths.reduce((accumulator, length) => accumulator + length, 0)

  console.log('The combined length of both pages is', totalLength)
});