Skip to content

Commit f14c912

Browse files
authoredJan 23, 2021
refactor: remove dependency on p-queue (#250)
1 parent 0433251 commit f14c912

File tree

4 files changed

+69
-64
lines changed

4 files changed

+69
-64
lines changed
 

‎package-lock.json

-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"compile": "tsc -p .",
1616
"test": "c8 mocha build/test",
1717
"fix": "gts fix",
18-
"codecov": "c8 report --reporter=json && codecov -f coverage/*.json",
1918
"lint": "gts lint",
2019
"build-binaries": "pkg . --out-path build/binaries",
2120
"docs-test": "npm link && linkinator ./README.md"
@@ -28,7 +27,6 @@
2827
"jsonexport": "^3.0.0",
2928
"marked": "^1.2.5",
3029
"meow": "^9.0.0",
31-
"p-queue": "^6.2.1",
3230
"serve-handler": "^6.1.3",
3331
"server-destroy": "^1.0.1",
3432
"update-notifier": "^5.0.0"

‎src/queue.ts

+53-30
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,75 @@
1-
import PQueue from 'p-queue';
1+
import {EventEmitter} from 'events';
22

33
export interface QueueOptions {
4-
concurrency?: number;
4+
concurrency: number;
55
}
66

77
export interface QueueItemOptions {
88
delay?: number;
99
}
1010

11+
interface QueueItem {
12+
fn: AsyncFunction;
13+
timeToRun: number;
14+
}
15+
16+
export declare interface Queue {
17+
on(event: 'done', listener: () => void): this;
18+
}
19+
1120
export type AsyncFunction = () => Promise<void>;
1221

13-
export class Queue {
14-
private q: PQueue;
15-
private activeTimers = 0;
22+
export class Queue extends EventEmitter {
23+
private q: Array<QueueItem> = [];
24+
private activeFunctions = 0;
25+
private concurrency: number;
1626

1727
constructor(options: QueueOptions) {
18-
this.q = new PQueue({
19-
concurrency: options.concurrency,
20-
});
28+
super();
29+
this.concurrency = options.concurrency;
2130
}
2231

2332
add(fn: AsyncFunction, options?: QueueItemOptions) {
24-
if (options?.delay) {
25-
setTimeout(() => {
26-
this.q.add(fn);
27-
this.activeTimers--;
28-
}, options.delay);
29-
this.activeTimers++;
30-
} else {
31-
this.q.add(fn);
32-
}
33+
const delay = options?.delay || 0;
34+
const timeToRun = Date.now() + delay;
35+
this.q.push({
36+
fn,
37+
timeToRun,
38+
});
39+
setTimeout(() => this.tick(), delay);
3340
}
3441

35-
async onIdle() {
36-
await this.q.onIdle();
37-
await new Promise<void>(resolve => {
38-
if (this.activeTimers === 0) {
39-
resolve();
42+
private tick() {
43+
// Check if we're complete
44+
if (this.activeFunctions === 0 && this.q.length === 0) {
45+
this.emit('done');
46+
return;
47+
}
48+
49+
for (let i = 0; i < this.q.length; i++) {
50+
// Check if we have too many concurrent functions executing
51+
if (this.activeFunctions >= this.concurrency) {
4052
return;
4153
}
42-
const timer = setInterval(async () => {
43-
if (this.activeTimers === 0) {
44-
await this.q.onIdle();
45-
clearInterval(timer);
46-
resolve();
47-
return;
48-
}
49-
}, 500);
54+
// grab the element at the front of the array
55+
const item = this.q.shift()!;
56+
// make sure this element is ready to execute - if not, to the back of the stack
57+
if (item.timeToRun > Date.now()) {
58+
this.q.push(item);
59+
} else {
60+
// this function is ready to go!
61+
this.activeFunctions++;
62+
item.fn().finally(() => {
63+
this.activeFunctions--;
64+
this.tick();
65+
});
66+
}
67+
}
68+
}
69+
70+
async onIdle() {
71+
return new Promise<void>(resolve => {
72+
this.on('done', () => resolve());
5073
});
5174
}
5275
}

‎test/test.retry.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@ describe('retries', () => {
3737

3838
const {promise, resolve} = invertedPromise();
3939
const checker = new LinkChecker().on('retry', resolve);
40-
const clock = sinon.useFakeTimers();
40+
const clock = sinon.useFakeTimers({
41+
shouldAdvanceTime: true,
42+
});
4143
const checkPromise = checker.check({
4244
path: 'test/fixtures/basic',
4345
retry: true,
4446
});
47+
4548
await promise;
4649
await clock.tickAsync(10_000);
4750
const results = await checkPromise;
@@ -62,7 +65,9 @@ describe('retries', () => {
6265

6366
const {promise, resolve} = invertedPromise();
6467
const checker = new LinkChecker().on('retry', resolve);
65-
const clock = sinon.useFakeTimers();
68+
const clock = sinon.useFakeTimers({
69+
shouldAdvanceTime: true,
70+
});
6671
const checkPromise = checker.check({
6772
path: 'test/fixtures/basic',
6873
retry: true,
@@ -85,7 +90,9 @@ describe('retries', () => {
8590

8691
const {promise, resolve} = invertedPromise();
8792
const checker = new LinkChecker().on('retry', resolve);
88-
const clock = sinon.useFakeTimers();
93+
const clock = sinon.useFakeTimers({
94+
shouldAdvanceTime: true,
95+
});
8996
const checkPromise = checker.check({
9097
path: 'test/fixtures/basic',
9198
retry: true,
@@ -125,7 +132,9 @@ describe('retries', () => {
125132

126133
const {promise, resolve} = invertedPromise();
127134
const checker = new LinkChecker().on('retry', resolve);
128-
const clock = sinon.useFakeTimers();
135+
const clock = sinon.useFakeTimers({
136+
shouldAdvanceTime: true,
137+
});
129138
const checkPromise = checker.check({
130139
path: 'test/fixtures/retry',
131140
recurse: true,
@@ -177,7 +186,9 @@ describe('retries', () => {
177186
r2();
178187
}
179188
});
180-
const clock = sinon.useFakeTimers();
189+
const clock = sinon.useFakeTimers({
190+
shouldAdvanceTime: true,
191+
});
181192
const checkPromise = checker.check({
182193
path: 'test/fixtures/retry',
183194
recurse: true,

0 commit comments

Comments
 (0)
Please sign in to comment.