-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathRequestQueue.js
78 lines (68 loc) · 2.41 KB
/
RequestQueue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import EventEmitter from 'events';
import onFinished from 'on-finished';
/*
* RequestQueue to ensure that only a single request is executing at a time.
*
* This middleware intercepts requests as they come in by delaying executing of
* next() until previous requests finish processing. This complements external
* server configuration via haproxy or similar that restricts concurrent
* requests. This per-process queue allows an application level guarantee of
* mutual exclusion of requests. This allows that behavior to be depended
* upon, allowing for safe (but careful) use of global state. Additionally,
* this allows for lifecycle hooks to be added for the periods when no request
* is currently executing, before or after the request has been run. These are
* ideal points to install behavior to reset global state or perform actions
* against the server at a "clean state" point in time.
*/
export default class RequestQueue extends EventEmitter {
constructor() {
super();
this.queue = [];
this.current = null;
this.outerMiddleware = this.outerMiddleware.bind(this);
this.innerMiddleware = this.innerMiddleware.bind(this);
this.finishCurrent = this.finishCurrent.bind(this);
}
process() {
if (!this.current) {
this.current = this.queue.shift();
this.emit('queueLength', this.queue.length);
if (this.current) {
this.emit('beforeRequest');
this.current.start();
}
} else {
this.emit('queueLength', this.queue.length);
}
}
/*
* Outer middleware must be the very first middleware installed on the app.
* This intercepts and begins queueing the request.
*/
outerMiddleware(req, res, next) {
const job = { req, res, start: next };
this.push(job);
}
/*
* Inner middleware must be last middleware installed before endpoints. This
* is only necessary because on-finished executes its callbacks in the order
* in which they were installed. We need this to be innermost so that we
* advance the queue only after the request and all other on-finished
* callbacks complete.
*
* Not adding this middleware will result in the queue never being drained.
*/
innerMiddleware(req, res, next) {
onFinished(res, this.finishCurrent);
next();
}
push(job) {
this.queue.push(job);
this.process();
}
finishCurrent() {
this.current = null;
this.emit('afterRequest');
this.process();
}
}