Semaphore
A primitive used to control access to a common resource by multiple tasks.
In this example, 5 tasks are spawned but only 2 can run concurrently.
import { semaphore, sleep } from 'ciorent';
const logTime = (...args: any[]) => { console.log('[' + performance.now().toFixed(1) + 'ms]', ...args);};
// Creates a semaphore with 2 permits// Each permit allows a task to access resource or to perform an operation concurrentlyconst sem = semaphore.init(2);
// Example taskconst runTask = async (id: number) => { // Acquire a permit or wait until a permit is available await semaphore.acquire(sem);
logTime(id, 'started'); await sleep(1000); logTime(id, 'done');
// Release the permit semaphore.release(sem);}
// Try to run 5 tasks concurrentlyfor (let i = 1; i <= 5; i++) runTask(i);
With semaphore the output will be:
[19.2ms] 1 started[19.4ms] 2 started[1020.0ms] 1 done[1020.5ms] 3 started[1020.6ms] 2 done[1020.6ms] 4 started[2022.0ms] 3 done[2022.2ms] 5 started[2022.3ms] 4 done[3023.2ms] 5 done
Without semaphore, all 5 tasks will start without waiting:
[18.2ms] 1 started[18.3ms] 2 started[18.3ms] 3 started[18.4ms] 4 started[18.4ms] 5 started[1019.2ms] 1 done[1019.4ms] 2 done[1019.6ms] 3 done[1019.6ms] 4 done[1019.6ms] 5 done
It is recommended to wrap semaphore.release
in a finally
block to release the permit when an error is thrown.
const runTask = async (...) => { await semaphore.acquire(sem);
try { // Code that can throw errors... } finally { semaphore.release(sem); }}