-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
101 lines (89 loc) · 2.72 KB
/
index.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
var EventEmitter = require('events');
module.exports = function (opt) {
var machinegun = new EventEmitter(),
shooting = 0,
magazine = [],
opt = opt || {};
var opt = {
barrels: typeof opt.barrels == 'undefined' ? 1 : opt.barrels,
giveUpOnError: typeof opt.giveUpOnError == 'undefined' ? false : opt.giveUpOnError,
ceaseFireOnEmpty: typeof opt.ceaseFireOnEmpty == 'undefined' ? false : opt.ceaseFireOnEmpty,
fireImmediately: typeof opt.fireImmediately == 'undefined' ? true : opt.fireImmediately
};
// State machine: firing, fireCeased, givenUp
var state = opt.fireImmediately ? 'firing' : 'fireCeased';
// Primer function wrapper class
// Used mainly to contextualise callback
var Cartridge = function (powder) {
var callback = function (err) {
shooting--;
if (err) {
// Only emit error if a listener exists
// This is mainly to prevent silent errors in promises
if (machinegun.listenerCount('error')) machinegun.emit('error', err);
if (opt.giveUpOnError) machinegun.giveUp(err);
}
trigger();
};
var primer = function () {
var maybeThenable = powder(callback);
if (maybeThenable && (typeof maybeThenable.then == 'function')) {
maybeThenable.then(callback.bind(null, null), function (err) { callback(err || new Error()); });
}
};
this.shoot = function () {
shooting++;
process.nextTick(primer);
};
};
var trigger = function () {
if (state == 'firing' && (!opt.barrels || opt.barrels > shooting)) {
if (magazine.length) {
// Below two lines easily justify weird naming convention around this module :)
magazine.shift().shoot();
trigger();
}
else if (!shooting) {
machinegun.emit('empty');
if (opt.ceaseFireOnEmpty) machinegun.ceaseFire();
}
}
};
// API methods
machinegun.load = function (primer) {
if (state != 'givenUp') process.nextTick(function () {
magazine.push(new Cartridge(primer));
trigger();
});
return machinegun;
};
machinegun.fire = function () {
if (state == 'fireCeased') process.nextTick(function () {
machinegun.emit('fire');
state = 'firing';
trigger();
});
return machinegun;
};
machinegun.ceaseFire = function () {
if (state == 'firing') {
machinegun.emit('ceaseFire');
state = 'fireCeased';
}
return machinegun;
};
machinegun.giveUp = function (reason) {
if (state != 'givenUp') {
machinegun.emit('giveUp', reason);
state = 'givenUp';
}
return machinegun;
};
machinegun.promise = function (value) {
return new Promise(function (resolve, reject) {
machinegun.on('empty', resolve.bind(null, value));
machinegun.on('giveUp', reject);
});
}
return machinegun;
};