UNPKG

@newdash/newdash

Version:

javascript/typescript utility library

132 lines (131 loc) 4.85 kB
"use strict"; // ref https://github.com/notenoughneon/await-semaphore // The MIT License (MIT) Object.defineProperty(exports, "__esModule", { value: true }); exports.Semaphore = void 0; // Copyright (c) 2016 Emma Kuo // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // copy for deno runtime const assert_1 = require("../assert"); class Semaphore { tasks = []; count; defaultAcquireTimeout = -1; /** * Semaphore implementation for async js operations, used for resource limit or pool implementation * * @since 5.15.0 * @category Functional * * @example * * ```ts * const sem = new Semaphore(10) * * async call_api(payload: any) { * const release = await sem.acquire() * // ... * // this block, will be execute with 10 concurrency limit * release() * } * * ``` */ constructor(count, defaultAcquireTimeout = -1) { (0, assert_1.mustProvide)(count, "count", "number"); this.count = count; this.defaultAcquireTimeout = defaultAcquireTimeout; } _schedule() { if (this.count > 0 && this.tasks.length > 0) { this.count--; const next = this.tasks.shift(); (0, assert_1.mustProvide)(next, "task", "function"); next(); } } /** * acquire a permit from sem * * @param timeout wait before timeout, if not set, will wait forever * @returns release function, which used for release a permit to sem */ acquire(timeout = this.defaultAcquireTimeout) { (0, assert_1.mustProvide)(timeout, "timeout", "number"); return new Promise((resolve, reject) => { // delay tasks const task = () => { if (task.hasTimeout) { // has timeout // resume semaphore this.count++; // re-schedule next one this._schedule(); } else { task.hasAcquired = true; // return acquire resolve(() => { // call by release if (!task.hasReleased) { task.hasReleased = true; this.count++; this._schedule(); } }); } }; task.hasTimeout = false; task.hasReleased = false; task.hasAcquired = false; // queue task this.tasks.push(task); setTimeout(() => this._schedule(), 0); if (typeof timeout === "number" && timeout > 0 && !isNaN(timeout) && isFinite(timeout)) { setTimeout(() => { if (!task.hasAcquired) { task.hasTimeout = true; reject(new Error(`semaphore acquire timeout: ${timeout}`)); } }, timeout); } }); } /** * run an async function with sem limit * * @param f async runner function * @param timeout wait timeout before wait * @returns */ async use(f, timeout = this.defaultAcquireTimeout) { (0, assert_1.mustProvide)(f, "f", "function"); (0, assert_1.mustProvide)(timeout, "timeout", "number"); return this .acquire(timeout) .then((release) => { const fResult = f(); if (fResult?.then !== undefined && typeof fResult?.then === "function") { return fResult.finally(release); } release(); return fResult; }); } } exports.Semaphore = Semaphore; exports.default = Semaphore;