1 of 13

Concurrency Control Proposal Overview

Michael Ficarra • TC39 TG5 • 11 October 2024

2 of 13

background & motivation

iterator helpers

async iterator helpers

unordered async�iterator helpers

concurrency control�(this Stage 1 proposal)

3 of 13

proposal goals

  1. concurrency support in AsyncIterator.prototype "consumer" methods
  2. a simple way to specify concurrency in the common case
  3. a way to split a managed resource across non-coordinating consumers
  4. a way to control concurrency for resources that change in capacity over time

4 of 13

AsyncIterator.prototype integration

limit(limit: ??)

an optional concurrency parameter added to all consuming methods

.toArray([ ?? ])

.forEach(fn [, ?? ])

.some(predicate [, ?? ])

.every(predicate [, ?? ])

.find(predicate [, ?? ])

.reduce(reducer [, initialValue [, ?? ]])

5 of 13

proposal goals

  • concurrency support in AsyncIterator.prototype "consumer" methods
  • a simple way to specify concurrency in the common case
  • a way to split a managed resource across non-coordinating consumers
  • a way to control concurrency for resources that change in capacity over time

6 of 13

AsyncIterator.prototype integration

buffered(limit: integer, prepopulate = false)

limit(limit: integer)

a concurrency parameter (integer) added to all consuming methods

.toArray([ limit ])

.forEach(fn [, limit ])

.some(predicate [, limit ])

.every(predicate [, limit ])

.find(predicate [, limit ])

.reduce(reducer [, initialValue [, limit ]])

7 of 13

proposal goals

  • concurrency support in AsyncIterator.prototype "consumer" methods
  • a simple way to specify concurrency in the common case
  • a way to split a managed resource across non-coordinating consumers
  • a way to control concurrency for resources that change in capacity over time

8 of 13

CountingGovernor(capacity: integer) class

acquire(): Promise<GovernorToken>

capacity parameter specifies number of concurrent live tokens

GovernorToken.prototype

release(): void === [Symbol.dispose](): void

9 of 13

AsyncIterator.prototype integration

buffered(limit: Governor | integer, prepopulate = false)

limit(limit: Governor | integer)

a concurrency parameter (Governor | integer) added to all consuming methods

.toArray([ governor ])

.forEach(fn [, governor ])

.some(predicate [, governor ])

.every(predicate [, governor ])

.find(predicate [, governor ])

.reduce(reducer [, initialValue [, governor ]])

10 of 13

proposal goals

  • concurrency support in AsyncIterator.prototype "consumer" methods
  • a simple way to specify concurrency in the common case
  • a way to split a managed resource across non-coordinating consumers
  • a way to control concurrency for resources that change in capacity over time

11 of 13

Governor interface

acquire(): Promise<GovernorToken>

GovernorToken.prototype

release(): void === [Symbol.dispose](): void

CountingGovernor(capacity: integer) class

implementing the Governor interface

capacity parameter specifies number of concurrent live tokens

12 of 13

how do I describe my concurrency limit?

simple concurrency

shared simple concurrency

complex concurrency

integer

CountingGovernor

a custom Governor

13 of 13

proposal goals: met

  • concurrency support in AsyncIterator.prototype "consumer" methods
    • met by additional parameters in .toArray, .forEach, .some, .every, .find, and .reduce on AsyncIterator.prototype
  • a simple way to specify concurrency in the common case
    • met by integer parameters
  • a way to split a managed resource across non-coordinating consumers
    • met by CountingGovernors
  • a way to control concurrency for resources that change in capacity over time
    • met by the Governor protocol