@leaddreamer/inventorylogic 0.0.3-beta.82 → 0.0.3-beta.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -673
- package/business/LineItem/index.js +2 -2
- package/business/Product/GoogleMerchantAPI.js +0 -2
- package/business/Utilities/index.js +0 -3
- package/business/boilerplate/index.js +1 -0
- package/package.json +4 -12
- package/business/Utilities/other.js +0 -404
- package/business/Utilities/promise-queue/index.js +0 -266
- package/business/Utilities/promise-queue/new.js +0 -231
- package/business/Utilities/rate-limited-queue/index.js +0 -127
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typedefs, constants, support functions and database operations related to Promise getQueueSize
|
|
3
|
-
* Thes are useful for scheduling large serial asynchronous operations
|
|
4
|
-
* @module PromiseQueue
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Creates a new rate limited queue. The queue is created with stopped state, i.e. you have to call start() to run jobs.
|
|
8
|
-
* @class
|
|
9
|
-
*/
|
|
10
|
-
export class PromiseQueueClass {
|
|
11
|
-
/**
|
|
12
|
-
* @param {number} maxCallsPerSecond - The rate limit of this queue in maximum calls per seconds which may be executed at any time. Pass values less than 1 to have less than one call per second, e.g. 1/3 for 3 one call in three seconds at maximum.
|
|
13
|
-
* @param {callback} logger optional logger function for errors
|
|
14
|
-
* @constructor
|
|
15
|
-
*/
|
|
16
|
-
constructor(maxCallsPerSecond=1, logger) {
|
|
17
|
-
/**
|
|
18
|
-
* The rate limit of this queue in maximum calls per seconds
|
|
19
|
-
* which may be executed at any time. Pass values less than 1
|
|
20
|
-
* to have less than one call per second, e.g. 1/3 for 3 one
|
|
21
|
-
* call in three seconds at maximum.
|
|
22
|
-
* @type {float}
|
|
23
|
-
*/
|
|
24
|
-
this.maxCallsPerSecond =
|
|
25
|
-
typeof maxCallsPerSecond === "undefined" ? 1 : maxCallsPerSecond;
|
|
26
|
-
/**
|
|
27
|
-
* optional logger function for errors
|
|
28
|
-
* @type {callback}
|
|
29
|
-
*/
|
|
30
|
-
this.logger = logger;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* lists of tasks to be processed, in order, in the queue
|
|
34
|
-
* @type {Array.callback}
|
|
35
|
-
*/
|
|
36
|
-
this.tasks = [];
|
|
37
|
-
/**
|
|
38
|
-
* flag to indicate queue processing is active
|
|
39
|
-
* @type {boolean}
|
|
40
|
-
*/
|
|
41
|
-
this.isActive = false;
|
|
42
|
-
/**
|
|
43
|
-
* pacing for queue processing / rate limiting
|
|
44
|
-
* @type {float}
|
|
45
|
-
*/
|
|
46
|
-
this.jobIntervalMillis = 1000 / maxCallsPerSecond;
|
|
47
|
-
/**
|
|
48
|
-
* timestamp of start of last task
|
|
49
|
-
* @type {time}
|
|
50
|
-
*/
|
|
51
|
-
this.lastTaskTimestamp = null;
|
|
52
|
-
/**
|
|
53
|
-
* @type {string}
|
|
54
|
-
*/
|
|
55
|
-
this.currentTaskTimeoutId = null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Processes the next task and returns the id of the time used to wait for the next invocation of this method.
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
updateTaskSchedule() {
|
|
63
|
-
if (this.logger && this.logger.debug) {
|
|
64
|
-
this.logger.debug("PromiseQueue.updateTaskSchedule");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (this.isEmpty() || this.isStopped()) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const queue = this;
|
|
72
|
-
|
|
73
|
-
function processNextTask() {
|
|
74
|
-
if (queue.logger && queue.logger.debug) {
|
|
75
|
-
queue.logger.debug("PromiseQueue.processNextTask", queue.tasks.length);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
var nextTaskItem = queue.tasks.shift();
|
|
79
|
-
if (nextTaskItem) {
|
|
80
|
-
try {
|
|
81
|
-
const result = nextTaskItem.task.call();
|
|
82
|
-
|
|
83
|
-
if (queue.logger && queue.logger.debug) {
|
|
84
|
-
queue.logger.debug(
|
|
85
|
-
"Resolving queued promise",
|
|
86
|
-
result,
|
|
87
|
-
result instanceof Promise
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
nextTaskItem.resolve(result);
|
|
92
|
-
} catch (e) {
|
|
93
|
-
queue.logger.info("rejecting queued promise result", e);
|
|
94
|
-
nextTaskItem.reject(e);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function scheduleNext(timeoutMillisOverride) {
|
|
100
|
-
if (queue.logger && queue.logger.debug) {
|
|
101
|
-
queue.logger.debug("PromiseQueue.scheduleNext", timeoutMillisOverride);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (queue.isEmpty()) {
|
|
105
|
-
queue.currentTaskTimeoutId = null;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
queue.currentTaskTimeoutId = setTimeout(function () {
|
|
110
|
-
queue.lastTaskTimestamp = Date.now();
|
|
111
|
-
processNextTask();
|
|
112
|
-
|
|
113
|
-
scheduleNext(queue.jobIntervalMillis);
|
|
114
|
-
}, timeoutMillisOverride);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (this.currentTaskTimeoutId == null) {
|
|
118
|
-
if (this.lastTaskTimestamp) {
|
|
119
|
-
let passedTime = Date.now() - this.lastTaskTimestamp;
|
|
120
|
-
if (passedTime > this.jobIntervalMillis) {
|
|
121
|
-
scheduleNext(this.jobIntervalMillis);
|
|
122
|
-
} else {
|
|
123
|
-
scheduleNext(Math.max(0, this.jobIntervalMillis - passedTime));
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
scheduleNext(0);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Starts the task processing of this queue.
|
|
133
|
-
*
|
|
134
|
-
* If it is already started, then false will be returned.
|
|
135
|
-
* If it is not yet started then the first task will be processed in the background and true will be returned.
|
|
136
|
-
*
|
|
137
|
-
* @return {boolean} true if this queue wasn't started before and now started to process tasks.
|
|
138
|
-
*/
|
|
139
|
-
start() {
|
|
140
|
-
if (this.isActive) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.isActive = true;
|
|
145
|
-
|
|
146
|
-
this.updateTaskSchedule();
|
|
147
|
-
|
|
148
|
-
return true;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Stops the processing of this queue.
|
|
153
|
-
* @return true if the queue was started and is now stopped. false if it was already stopped.
|
|
154
|
-
*/
|
|
155
|
-
stop() {
|
|
156
|
-
if (this.isActive) {
|
|
157
|
-
this.isActive = false;
|
|
158
|
-
|
|
159
|
-
if (this.currentTaskTimeoutId) {
|
|
160
|
-
clearInterval(this.currentTaskTimeoutId);
|
|
161
|
-
this.currentTaskTimeoutId = null;
|
|
162
|
-
this.lastTaskTimestamp = null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
getMaxCallsPerSecond() {
|
|
168
|
-
return this.maxCallsPerSecond;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
isStarted() {
|
|
172
|
-
return this.isActive;
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
isStopped() {
|
|
176
|
-
return !this.isStarted();
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* @returns {boolean} true if this queue is currently empty, i.e. if it has no tasks in the queue.
|
|
181
|
-
*/
|
|
182
|
-
isEmpty() {
|
|
183
|
-
return this.tasks.length === 0;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* @returns {boolean} true if this queue has one or more entries.
|
|
188
|
-
*/
|
|
189
|
-
isNotEmpty() {
|
|
190
|
-
return !this.isEmpty();
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* @param {function} task - The task to add.
|
|
195
|
-
* @param {boolean} append - if the task should be appended or prepended to the list of queued tasks
|
|
196
|
-
* @return {Promise} A promise which is resolved/rejected when the task was executed by the queue.
|
|
197
|
-
*/
|
|
198
|
-
async add(task, append) {
|
|
199
|
-
var self = this;
|
|
200
|
-
|
|
201
|
-
var promise = new Promise(function (resolve, reject) {
|
|
202
|
-
var newTaskItem = { task: task, resolve: resolve, reject: reject };
|
|
203
|
-
|
|
204
|
-
if (self.isEmpty() || append) {
|
|
205
|
-
self.tasks.push(newTaskItem);
|
|
206
|
-
} else {
|
|
207
|
-
self.tasks.unshift(newTaskItem);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
self.updateTaskSchedule();
|
|
212
|
-
|
|
213
|
-
return promise;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Appends a new task at the end of the queue.
|
|
218
|
-
* @param {function} task - The task to perform. This function will be called with no arguments.
|
|
219
|
-
* @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
|
|
220
|
-
*/
|
|
221
|
-
async append(task) {
|
|
222
|
-
return this.add(task, true);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Adds a new task as first item in the queue.
|
|
227
|
-
* @param task
|
|
228
|
-
* @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
|
|
229
|
-
*/
|
|
230
|
-
async prepend(task) {
|
|
231
|
-
return this.add(task, false);
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Removes the task from the qeueue. If the task is contained more than one time then all occurrences will be removed.
|
|
236
|
-
* @param {function} task - The task to remove.
|
|
237
|
-
* @return {number} - The number of items which were removed.
|
|
238
|
-
*/
|
|
239
|
-
remove(task) {
|
|
240
|
-
if (this.isEmpty()) {
|
|
241
|
-
return 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
var removedItems = 0;
|
|
245
|
-
for (var i = this.tasks.length - 1; i >= 0; i--) {
|
|
246
|
-
if (this.tasks[i].task === task) {
|
|
247
|
-
this.tasks.splice(i, 1);
|
|
248
|
-
removedItems++;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return removedItems;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* @returns {Number} - The number of queued/waiting tasks in the queue.
|
|
257
|
-
*/
|
|
258
|
-
getQueueSize() {
|
|
259
|
-
return this.tasks.length;
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
var PromiseQueue = new PromiseQueueClass(1, null);
|
|
265
|
-
|
|
266
|
-
export default PromiseQueue;
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typedefs, constants, support functions and database operations related to Promise getQueueSize
|
|
3
|
-
* Thes are useful for scheduling large serial asynchronous operations
|
|
4
|
-
* @module PromiseQueue
|
|
5
|
-
*/
|
|
6
|
-
(function () {
|
|
7
|
-
/**
|
|
8
|
-
* Creates a new rate limited queue. The queue is created with stopped state, i.e. you have to call start() to run jobs.
|
|
9
|
-
* @param {number} maxCallsPerSecond=1 - The rate limit of this queue in maximum calls per seconds which may be executed at any time. Pass values less than 1 to have less than one call per second, e.g. 1/3 for 3 one call in three seconds at maximum.
|
|
10
|
-
* @param logger
|
|
11
|
-
* @constructor
|
|
12
|
-
*/
|
|
13
|
-
function PromiseQueue(maxCallsPerSecond, logger) {
|
|
14
|
-
this.maxCallsPerSecond =
|
|
15
|
-
typeof maxCallsPerSecond === "undefined" ? 1 : maxCallsPerSecond;
|
|
16
|
-
this.logger = logger;
|
|
17
|
-
|
|
18
|
-
this.tasks = [];
|
|
19
|
-
this.isActive = false;
|
|
20
|
-
this.jobIntervalMillis = 1000 / maxCallsPerSecond;
|
|
21
|
-
|
|
22
|
-
this.lastTaskTimestamp = null;
|
|
23
|
-
this.currentTaskTimeoutId = null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Processes the next task and returns the id of the time used to wait for the next invocation of this method.
|
|
28
|
-
* @private
|
|
29
|
-
*/
|
|
30
|
-
PromiseQueue.prototype.updateTaskSchedule = function () {
|
|
31
|
-
if (this.logger && this.logger.debug) {
|
|
32
|
-
this.logger.debug("PromiseQueue.updateTaskSchedule");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (this.isEmpty() || this.isStopped()) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const queue = this;
|
|
40
|
-
|
|
41
|
-
function processNextTask() {
|
|
42
|
-
if (queue.logger && queue.logger.debug) {
|
|
43
|
-
queue.logger.debug("PromiseQueue.processNextTask", queue.tasks.length);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
var nextTaskItem = queue.tasks.shift();
|
|
47
|
-
if (nextTaskItem) {
|
|
48
|
-
try {
|
|
49
|
-
const result = nextTaskItem.task.call();
|
|
50
|
-
|
|
51
|
-
if (queue.logger && queue.logger.debug) {
|
|
52
|
-
queue.logger.debug(
|
|
53
|
-
"Resolving queued promise",
|
|
54
|
-
result,
|
|
55
|
-
result instanceof Promise
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
nextTaskItem.resolve(result);
|
|
60
|
-
} catch (e) {
|
|
61
|
-
queue.logger.info("rejecting queued promise result", e);
|
|
62
|
-
nextTaskItem.reject(e);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function scheduleNext(timeoutMillisOverride) {
|
|
68
|
-
if (queue.logger && queue.logger.debug) {
|
|
69
|
-
queue.logger.debug("PromiseQueue.scheduleNext", timeoutMillisOverride);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (queue.isEmpty()) {
|
|
73
|
-
queue.currentTaskTimeoutId = null;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
queue.currentTaskTimeoutId = setTimeout(function () {
|
|
78
|
-
queue.lastTaskTimestamp = Date.now();
|
|
79
|
-
processNextTask();
|
|
80
|
-
|
|
81
|
-
scheduleNext(queue.jobIntervalMillis);
|
|
82
|
-
}, timeoutMillisOverride);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (this.currentTaskTimeoutId == null) {
|
|
86
|
-
if (this.lastTaskTimestamp) {
|
|
87
|
-
let passedTime = Date.now() - this.lastTaskTimestamp;
|
|
88
|
-
if (passedTime > this.jobIntervalMillis) {
|
|
89
|
-
scheduleNext(this.jobIntervalMillis);
|
|
90
|
-
} else {
|
|
91
|
-
scheduleNext(Math.max(0, this.jobIntervalMillis - passedTime));
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
scheduleNext(0);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Starts the task processing of this queue.
|
|
101
|
-
*
|
|
102
|
-
* If it is already started, then false will be returned.
|
|
103
|
-
* If it is not yet started then the first task will be processed in the background and true will be returned.
|
|
104
|
-
*
|
|
105
|
-
* @return {boolean} true if this queue wasn't started before and now started to process tasks.
|
|
106
|
-
*/
|
|
107
|
-
PromiseQueue.prototype.start = function () {
|
|
108
|
-
if (this.isActive) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
this.isActive = true;
|
|
113
|
-
|
|
114
|
-
this.updateTaskSchedule();
|
|
115
|
-
|
|
116
|
-
return true;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Stops the processing of this queue.
|
|
121
|
-
* @return true if the queue was started and is now stopped. false if it was already stopped.
|
|
122
|
-
*/
|
|
123
|
-
PromiseQueue.prototype.stop = function () {
|
|
124
|
-
if (this.isActive) {
|
|
125
|
-
this.isActive = false;
|
|
126
|
-
|
|
127
|
-
if (this.currentTaskTimeoutId) {
|
|
128
|
-
clearInterval(this.currentTaskTimeoutId);
|
|
129
|
-
this.currentTaskTimeoutId = null;
|
|
130
|
-
this.lastTaskTimestamp = null;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
PromiseQueue.prototype.getMaxCallsPerSecond = function () {
|
|
136
|
-
return this.maxCallsPerSecond;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
PromiseQueue.prototype.isStarted = function () {
|
|
140
|
-
return this.isActive;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
PromiseQueue.prototype.isStopped = function () {
|
|
144
|
-
return !this.isStarted();
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @returns {boolean} true if this queue is currently empty, i.e. if it has no tasks in the queue.
|
|
149
|
-
*/
|
|
150
|
-
PromiseQueue.prototype.isEmpty = function () {
|
|
151
|
-
return this.tasks.length === 0;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* @returns {boolean} true if this queue has one or more entries.
|
|
156
|
-
*/
|
|
157
|
-
PromiseQueue.prototype.isNotEmpty = function () {
|
|
158
|
-
return !this.isEmpty();
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* @param {function} task - The task to add.
|
|
163
|
-
* @param {boolean} append - if the task should be appended or prepended to the list of queued tasks
|
|
164
|
-
* @return {Promise} A promise which is resolved/rejected when the task was executed by the queue.
|
|
165
|
-
*/
|
|
166
|
-
PromiseQueue.prototype.add = function (task, append) {
|
|
167
|
-
var self = this;
|
|
168
|
-
|
|
169
|
-
var promise = new Promise(function (resolve, reject) {
|
|
170
|
-
var newTaskItem = { task: task, resolve: resolve, reject: reject };
|
|
171
|
-
|
|
172
|
-
if (self.isEmpty() || append) {
|
|
173
|
-
self.tasks.push(newTaskItem);
|
|
174
|
-
} else {
|
|
175
|
-
self.tasks.unshift(newTaskItem);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
self.updateTaskSchedule();
|
|
180
|
-
|
|
181
|
-
return promise;
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Appends a new task at the end of the queue.
|
|
186
|
-
* @param {function} task - The task to perform. This function will be called with no arguments.
|
|
187
|
-
* @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
|
|
188
|
-
*/
|
|
189
|
-
PromiseQueue.prototype.append = function (task) {
|
|
190
|
-
return this.add(task, true);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Adds a new task as first item in the queue.
|
|
195
|
-
* @param task
|
|
196
|
-
* @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
|
|
197
|
-
*/
|
|
198
|
-
PromiseQueue.prototype.prepend = function (task) {
|
|
199
|
-
return this.add(task, false);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Removes the task from the qeueue. If the task is contained more than one time then all occurrences will be removed.
|
|
204
|
-
* @param {function} task - The task to remove.
|
|
205
|
-
* @return {number} - The number of items which were removed.
|
|
206
|
-
*/
|
|
207
|
-
PromiseQueue.prototype.remove = function (task) {
|
|
208
|
-
if (this.isEmpty()) {
|
|
209
|
-
return 0;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
var removedItems = 0;
|
|
213
|
-
for (var i = this.tasks.length - 1; i >= 0; i--) {
|
|
214
|
-
if (this.tasks[i].task === task) {
|
|
215
|
-
this.tasks.splice(i, 1);
|
|
216
|
-
removedItems++;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return removedItems;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* @returns {Number} - The number of queued/waiting tasks in the queue.
|
|
225
|
-
*/
|
|
226
|
-
PromiseQueue.prototype.getQueueSize = function () {
|
|
227
|
-
return this.tasks.length;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
module.exports = PromiseQueue;
|
|
231
|
-
})();
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
function createQueue(
|
|
2
|
-
slidingWindowInterval,
|
|
3
|
-
maxTasksInSlidingWindow,
|
|
4
|
-
maxConcurrent = Infinity
|
|
5
|
-
) {
|
|
6
|
-
const q = [];
|
|
7
|
-
|
|
8
|
-
let runningTasks = 0;
|
|
9
|
-
|
|
10
|
-
function enqueue(taskHandlers) {
|
|
11
|
-
if ([].concat(taskHandlers).length === 0) {
|
|
12
|
-
return Promise.resolve([]);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
taskHandlers = Array.isArray(taskHandlers) ? taskHandlers : [taskHandlers];
|
|
16
|
-
const newTasks = [];
|
|
17
|
-
for (const handler of taskHandlers) {
|
|
18
|
-
if (typeof handler !== "function") {
|
|
19
|
-
throw "Task must be a function";
|
|
20
|
-
}
|
|
21
|
-
const newTask = {
|
|
22
|
-
handler: handler,
|
|
23
|
-
startedAt: -1,
|
|
24
|
-
promise: undefined
|
|
25
|
-
};
|
|
26
|
-
q.push(newTask);
|
|
27
|
-
newTasks.push(newTask);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const promise = promiseAll(newTasks);
|
|
31
|
-
|
|
32
|
-
runNextTaskIfPossible();
|
|
33
|
-
|
|
34
|
-
promise.finally(() => {
|
|
35
|
-
if (runningTasks === 0 && pendingTasks().length === 0) {
|
|
36
|
-
q.splice(0, q.length);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return promise;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function pendingTasks() {
|
|
43
|
-
return q.filter((task) => task.startedAt === -1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function runNextTaskIfPossible() {
|
|
47
|
-
if (runningTasks >= maxConcurrent) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const tasksInSlidingWindow = q.filter(
|
|
52
|
-
(task) =>
|
|
53
|
-
task.startedAt >= 0 &&
|
|
54
|
-
task.startedAt > Date.now() - slidingWindowInterval
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const availableSlotsInSlidingWindow =
|
|
58
|
-
maxTasksInSlidingWindow - tasksInSlidingWindow.length;
|
|
59
|
-
|
|
60
|
-
const nextTask = pendingTasks()[0];
|
|
61
|
-
if (!nextTask) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function runNextTask() {
|
|
66
|
-
++runningTasks;
|
|
67
|
-
nextTask.startedAt = Date.now();
|
|
68
|
-
nextTask.promise = Promise.resolve(nextTask.handler())
|
|
69
|
-
.catch((e) => e)
|
|
70
|
-
.finally(() => {
|
|
71
|
-
--runningTasks;
|
|
72
|
-
runNextTaskIfPossible();
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (availableSlotsInSlidingWindow > 0) {
|
|
77
|
-
runNextTask();
|
|
78
|
-
runNextTaskIfPossible();
|
|
79
|
-
} else {
|
|
80
|
-
setTimeout(
|
|
81
|
-
runNextTaskIfPossible,
|
|
82
|
-
slidingWindowInterval -
|
|
83
|
-
(Date.now() - tasksInSlidingWindow[0].startedAt) +
|
|
84
|
-
1
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (process.env.NODE_ENV === "TEST") {
|
|
90
|
-
enqueue.internals = () => ({
|
|
91
|
-
q,
|
|
92
|
-
runningTasks
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return enqueue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function promiseAll(newTasks) {
|
|
100
|
-
let batchPromiseCreated = false;
|
|
101
|
-
|
|
102
|
-
return new Promise((batchResolve, batchReject) => {
|
|
103
|
-
for (const task of newTasks) {
|
|
104
|
-
const originalHandler = task.handler;
|
|
105
|
-
task.handler = () =>
|
|
106
|
-
new Promise((resolve, reject) => {
|
|
107
|
-
Promise.resolve(originalHandler())
|
|
108
|
-
.then(resolve)
|
|
109
|
-
.catch(reject)
|
|
110
|
-
.finally(() => {
|
|
111
|
-
if (batchPromiseCreated) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const pendingTasks = newTasks.filter((t) => !t.promise);
|
|
115
|
-
if (pendingTasks.length === 0) {
|
|
116
|
-
batchPromiseCreated = true;
|
|
117
|
-
return Promise.all(newTasks.map((t) => t.promise))
|
|
118
|
-
.then(batchResolve)
|
|
119
|
-
.catch(batchReject);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
module.exports = createQueue;
|