|
|
@ -1,41 +1,84 @@ |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Responsible for sequentially executing actions on the database |
|
|
|
* Responsible for sequentially executing actions on the database |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
const async = require('async') |
|
|
|
|
|
|
|
|
|
|
|
const makeQueue = execute => { |
|
|
|
|
|
|
|
const tasks = new Map() |
|
|
|
|
|
|
|
let running = false |
|
|
|
|
|
|
|
let drainPromise = Promise.resolve() |
|
|
|
|
|
|
|
const executeNextTask = async (self = false) => { |
|
|
|
|
|
|
|
if (!tasks.size) { |
|
|
|
|
|
|
|
running = false |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} else if (running && !self) { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
running = true |
|
|
|
|
|
|
|
const [task, { resolve, reject }] = tasks[Symbol.iterator]().next().value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tasks.delete(task) |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
resolve(await execute(task)) |
|
|
|
|
|
|
|
} catch (err) { |
|
|
|
|
|
|
|
reject(err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
drainPromise = executeNextTask(true) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
push (task) { |
|
|
|
|
|
|
|
let _resolve, _reject |
|
|
|
|
|
|
|
const promise = new Promise((resolve, reject) => { |
|
|
|
|
|
|
|
_reject = reject |
|
|
|
|
|
|
|
_resolve = resolve |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
tasks.set(task, { resolve: _resolve, reject: _reject }) |
|
|
|
|
|
|
|
if (!running) drainPromise = executeNextTask() |
|
|
|
|
|
|
|
return promise |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
async drain () { |
|
|
|
|
|
|
|
return drainPromise |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class Executor { |
|
|
|
class Executor { |
|
|
|
constructor () { |
|
|
|
constructor () { |
|
|
|
this.buffer = [] |
|
|
|
this.buffer = [] |
|
|
|
this.ready = false |
|
|
|
this.ready = false |
|
|
|
|
|
|
|
|
|
|
|
// This queue will execute all commands, one-by-one in order
|
|
|
|
this.queue = makeQueue(async task => { |
|
|
|
this.queue = async.queue((task, cb) => { |
|
|
|
|
|
|
|
// task.arguments is an array-like object on which adding a new field doesn't work, so we transform it into a real array
|
|
|
|
// task.arguments is an array-like object on which adding a new field doesn't work, so we transform it into a real array
|
|
|
|
const newArguments = Array.from(task.arguments) |
|
|
|
const newArguments = Array.from(task.arguments) |
|
|
|
|
|
|
|
|
|
|
|
const lastArg = newArguments[newArguments.length - 1] |
|
|
|
// If the task isn't async, let's proceed with the old handler
|
|
|
|
|
|
|
|
if (!task.async) { |
|
|
|
// Always tell the queue task is complete. Execute callback if any was given.
|
|
|
|
const lastArg = newArguments[newArguments.length - 1] |
|
|
|
if (typeof lastArg === 'function') { |
|
|
|
await new Promise(resolve => { |
|
|
|
// Callback was supplied
|
|
|
|
if (typeof lastArg === 'function') { |
|
|
|
newArguments[newArguments.length - 1] = function () { |
|
|
|
// We got a callback
|
|
|
|
if (typeof setImmediate === 'function') { |
|
|
|
newArguments.pop() // remove original callback
|
|
|
|
setImmediate(cb) |
|
|
|
task.fn.apply(task.this, [...newArguments, function () { |
|
|
|
|
|
|
|
resolve() // triggers next task after next tick
|
|
|
|
|
|
|
|
lastArg.apply(null, arguments) // call original callback
|
|
|
|
|
|
|
|
}]) |
|
|
|
|
|
|
|
} else if (!lastArg && task.arguments.length !== 0) { |
|
|
|
|
|
|
|
// We got a falsy callback
|
|
|
|
|
|
|
|
newArguments.pop() // remove original callback
|
|
|
|
|
|
|
|
task.fn.apply(task.this, [...newArguments, () => { |
|
|
|
|
|
|
|
resolve() |
|
|
|
|
|
|
|
}]) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
process.nextTick(cb) |
|
|
|
// We don't have a callback
|
|
|
|
|
|
|
|
task.fn.apply(task.this, [...newArguments, () => { |
|
|
|
|
|
|
|
resolve() |
|
|
|
|
|
|
|
}]) |
|
|
|
} |
|
|
|
} |
|
|
|
lastArg.apply(null, arguments) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
} else if (!lastArg && task.arguments.length !== 0) { |
|
|
|
|
|
|
|
// false/undefined/null supplied as callback
|
|
|
|
|
|
|
|
newArguments[newArguments.length - 1] = () => { cb() } |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// Nothing supplied as callback
|
|
|
|
await task.fn.apply(task.this, newArguments) |
|
|
|
newArguments.push(() => { cb() }) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
task.fn.apply(task.this, newArguments) |
|
|
|
|
|
|
|
}, 1) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|