UNPKG

async-scheduler

Version:

![CI](https://github.com/kremi151/async-scheduler/workflows/CI/badge.svg) ![NPM](https://img.shields.io/npm/v/async-scheduler?color=green)

246 lines (245 loc) 10.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = require("chai"); require("mocha"); const Scheduler_1 = __importDefault(require("./Scheduler")); const SchedulableTask_1 = require("./SchedulableTask"); function wait(timeout) { return new Promise((resolve) => { setTimeout(resolve, timeout); }); } describe('Scheduler', () => { it('should execute tasks in numerical order', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(5); // Create array with numbers from 0 to 20 let sortedNumbers = []; let shuffledNumbers = []; for (let i = 20; i >= 0; i--) { sortedNumbers.push(i); shuffledNumbers.push(i); } // Shuffle array shuffledNumbers.sort(() => Math.random() - 0.5); let executionOrder = []; let promises = shuffledNumbers.map((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr), 10 * Math.random()); }); }, onPreExecute() { executionOrder.push(nbr); } })); yield Promise.all(promises); chai_1.expect(executionOrder).to.eql(sortedNumbers); })); it('should run only a limited amount of tasks in parallel', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(2); let numbers = [1, 2, 3, 4, 5]; let executionOrder = []; numbers.forEach((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr), 10); }); }, onPreExecute() { executionOrder.push(nbr); } })); yield wait(5); chai_1.expect(scheduler.executingTasks).to.equal(2); chai_1.expect(executionOrder).to.eql([5, 4]); yield wait(10); chai_1.expect(scheduler.executingTasks).to.equal(2); chai_1.expect(executionOrder).to.eql([5, 4, 3, 2]); yield wait(10); chai_1.expect(scheduler.executingTasks).to.equal(1); chai_1.expect(executionOrder).to.eql([5, 4, 3, 2, 1]); })); it('should be ready to execute again after all tasks have been executed', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(2); let promises = [1, 2, 3, 4, 5].map((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => resolve(nbr)); } })); let result = yield Promise.all(promises); chai_1.expect(result).to.eql([1, 2, 3, 4, 5]); promises = [6, 7, 8, 9, 10].map((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => resolve(nbr)); } })); result = yield Promise.all(promises); chai_1.expect(result).to.eql([6, 7, 8, 9, 10]); })); it('should accept and execute new tasks while executing other ones', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(2); let executionOrder = []; let promises = [1, 2, 3, 6, 7].map((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr), 10); }); }, onPreExecute() { executionOrder.push(nbr); } })); yield wait(5); promises = [ ...promises, ...([4, 5].map((nbr) => scheduler.enqueue({ priority: nbr, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr), 10); }); }, onPreExecute() { executionOrder.push(nbr); } }))) ]; yield Promise.all(promises); chai_1.expect(executionOrder).to.eql([7, 6, 5, 4, 3, 2, 1]); })); it('should cancel tasks based on mutex collisions', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(2); let allPromises = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((nbr) => scheduler.enqueue({ priority: 0, mutex: 1 << Math.floor(nbr / 2), meta: { extraVarNumber: nbr, }, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr)); }); }, onTaskCollision(other) { chai_1.expect(other.meta).to.be.ok; if (other.meta.extraVarNumber <= nbr) { return SchedulableTask_1.TaskCollisionStrategy.KEEP_OTHER; } else { return SchedulableTask_1.TaskCollisionStrategy.KEEP_THIS; } } }).catch(() => -1 * nbr)); let result = yield Promise.all(allPromises); chai_1.expect(result).to.eql([0, -1, 2, -3, 4, -5, 6, -7, 8, -9]); })); it('should allow tasks to resolve each other in case of collision', () => __awaiter(void 0, void 0, void 0, function* () { let scheduler = new Scheduler_1.default(2); let allPromises = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((nbr) => scheduler.enqueue({ priority: 0, mutex: 1 << Math.floor(nbr / 2), meta: { extraVarNumber: nbr, }, execute() { return new Promise((resolve) => { setTimeout(() => resolve(nbr)); }); }, onTaskCollision(other) { chai_1.expect(other.meta).to.be.ok; if (other.meta.extraVarNumber <= nbr) { return SchedulableTask_1.TaskCollisionStrategy.RESOLVE_OTHER; } else { return SchedulableTask_1.TaskCollisionStrategy.RESOLVE_THIS; } } })); let result = yield Promise.all(allPromises); chai_1.expect(result).to.eql([0, 0, 2, 2, 4, 4, 6, 6, 8, 8]); })); it('should trigger idle listeners correctly when every task succeeds', () => __awaiter(void 0, void 0, void 0, function* () { const scheduler = new Scheduler_1.default(2); const executed = []; [0, 1, 2, 3].forEach(i => { scheduler.enqueue(() => new Promise((resolve) => { setTimeout(() => { [0, 1, 2, 3].forEach(j => { scheduler.enqueue(() => new Promise((subResolve) => { setTimeout(() => { executed.push((i * 10) + j); subResolve(); }); })); }); resolve(); }); })); }); yield scheduler.waitForIdle(); executed.sort((a, b) => a - b); chai_1.expect(executed).to.deep.equal([0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33]); })); it('should trigger idle listeners correctly when some tasks fail', () => __awaiter(void 0, void 0, void 0, function* () { const scheduler = new Scheduler_1.default(2); const executed = []; [0, 1, 2, 3].forEach(i => { scheduler.enqueue(() => new Promise((resolve) => { setTimeout(() => { [0, 1, 2, 3].forEach(j => { scheduler.enqueue(() => new Promise((subResolve, subReject) => { setTimeout(() => { const n = (i * 10) + j; if (n % 2 === 0) { executed.push(n); subResolve(); } else { subReject(new Error('oof')); } }); })).catch(() => { }); }); resolve(); }); })); }); yield scheduler.waitForIdle(); executed.sort((a, b) => a - b); chai_1.expect(executed).to.deep.equal([0, 2, 10, 12, 20, 22, 30, 32]); })); it('should resolve waitForIdle directly if already idle', () => __awaiter(void 0, void 0, void 0, function* () { const scheduler = new Scheduler_1.default(2); yield scheduler.waitForIdle(); })); it('should resolve waitForIdle only when tasks have finished if n(tasks) < n(maxTasks)', () => __awaiter(void 0, void 0, void 0, function* () { const scheduler = new Scheduler_1.default(4); const results = []; scheduler.enqueue(() => new Promise((resolve) => { setTimeout(() => { results.push(42); resolve(); }); })).then(() => { }); yield scheduler.waitForIdle(); chai_1.expect(results).to.deep.equal([42]); })); });