Concurrency Options
On this page
Effect offers various options to manage how effects are executed and control the overall operation's result. These options help determine how many effects can run at the same time concurrently.
ts
type Options = {readonly concurrency?: Concurrency/* ... other options ... */}
ts
type Options = {readonly concurrency?: Concurrency/* ... other options ... */}
In this section, we'll focus on the option that handles concurrency, which is the concurrency
option with a type of Concurrency
:
ts
type Concurrency = number | "unbounded" | "inherit"
ts
type Concurrency = number | "unbounded" | "inherit"
Let's understand the meaning of each configuration value.
The following examples use the Effect.all
function, but the concept
applies to many other Effect APIs that accept concurrency options, such as
Effect.forEach
.
Sequential Execution (Default)
By default, if you don't specify any concurrency option, effects will run sequentially, one after the other. This means each effect starts only after the previous one completes.
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")constsequential =Effect .all ([task1 ,task2 ])Effect .runPromise (sequential )/*Output:start task1task1 donestart task2 <-- task2 starts only after task1 completestask2 done*/
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")constsequential =Effect .all ([task1 ,task2 ])Effect .runPromise (sequential )/*Output:start task1task1 donestart task2 <-- task2 starts only after task1 completestask2 done*/
Numbered Concurrency
You can control the number of concurrent operations with the concurrency
option. For example, with concurrency: 2
, up to 2 effects will run simultaneously.
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constnumber =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : 2})Effect .runPromise (number )/*Output:start task1start task2 <-- active tasks: task1, task2task2 donestart task3 <-- active tasks: task1, task3task1 donestart task4 <-- active tasks: task3, task4task4 donestart task5 <-- active tasks: task3, task5task3 donetask5 done*/
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constnumber =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : 2})Effect .runPromise (number )/*Output:start task1start task2 <-- active tasks: task1, task2task2 donestart task3 <-- active tasks: task1, task3task1 donestart task4 <-- active tasks: task3, task4task4 donestart task5 <-- active tasks: task3, task5task3 donetask5 done*/
Unbounded Concurrency
If you set concurrency: "unbounded"
, as many effects as needed will run concurrently, without any specific limit.
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constunbounded =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "unbounded"})Effect .runPromise (unbounded )/*Output:start task1start task2start task3start task4start task5task2 donetask4 donetask5 donetask1 donetask3 done*/
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constunbounded =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "unbounded"})Effect .runPromise (unbounded )/*Output:start task1start task2start task3start task4start task5task2 donetask4 donetask5 donetask1 donetask3 done*/
Inherit Concurrency
The concurrency: "inherit"
option adapts based on context, controlled by Effect.withConcurrency(number | "unbounded")
.
If there's no Effect.withConcurrency
call, the default is "unbounded"
. Otherwise, it inherits the configuration set by Effect.withConcurrency
.
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constinherit =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "inherit"})Effect .runPromise (inherit )/*Output:start task1start task2start task3start task4start task5task2 donetask4 donetask5 donetask1 donetask3 done*/
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constinherit =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "inherit"})Effect .runPromise (inherit )/*Output:start task1start task2start task3start task4start task5task2 donetask4 donetask5 donetask1 donetask3 done*/
If you use Effect.withConcurrency
, it will adopt that specific concurrency configuration.
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constinherit =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "inherit"})constwithConcurrency =inherit .pipe (Effect .withConcurrency (2))Effect .runPromise (withConcurrency )/*Output:start task1start task2 <-- active tasks: task1, task2task2 donestart task3 <-- active tasks: task1, task3task1 donestart task4 <-- active tasks: task3, task4task4 donestart task5 <-- active tasks: task3, task5task3 donetask5 done*/
ts
import {Effect ,Duration } from "effect"constmakeTask = (n : number,delay :Duration .DurationInput ) =>Effect .promise (() =>newPromise <void>((resolve ) => {console .log (`start task${n }`)setTimeout (() => {console .log (`task${n } done`)resolve ()},Duration .toMillis (delay ))}))consttask1 =makeTask (1, "200 millis")consttask2 =makeTask (2, "100 millis")consttask3 =makeTask (3, "210 millis")consttask4 =makeTask (4, "110 millis")consttask5 =makeTask (5, "150 millis")constinherit =Effect .all ([task1 ,task2 ,task3 ,task4 ,task5 ], {concurrency : "inherit"})constwithConcurrency =inherit .pipe (Effect .withConcurrency (2))Effect .runPromise (withConcurrency )/*Output:start task1start task2 <-- active tasks: task1, task2task2 donestart task3 <-- active tasks: task1, task3task1 donestart task4 <-- active tasks: task3, task4task4 donestart task5 <-- active tasks: task3, task5task3 donetask5 done*/