itns-sidechain/lib/utils/asyncobject.js

235 lines
4.3 KiB
JavaScript
Raw Normal View History

/*!
* async.js - async object class for bcoin
2017-02-03 22:47:26 -08:00
* Copyright (c) 2016-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
2016-11-19 10:45:31 -08:00
var util = require('./util');
var Lock = require('./lock');
/**
* An abstract object that handles state and
* provides recallable open and close methods.
2017-02-03 22:47:26 -08:00
* @alias module:utils.AsyncObject
* @constructor
* @property {Boolean} loading
* @property {Boolean} closing
* @property {Boolean} loaded
*/
function AsyncObject() {
assert(this instanceof AsyncObject);
EventEmitter.call(this);
this._asyncLock = new Lock();
this._hooks = Object.create(null);
2016-11-10 13:33:40 -08:00
this.loading = false;
this.closing = false;
this.loaded = false;
}
2016-11-19 10:45:31 -08:00
util.inherits(AsyncObject, EventEmitter);
/**
* Open the object (recallable).
2017-02-03 22:47:26 -08:00
* @method
2016-09-23 01:05:06 -07:00
* @returns {Promise}
*/
AsyncObject.prototype.open = async function open() {
var unlock = await this._asyncLock.lock();
2016-11-10 13:33:40 -08:00
try {
return await this.__open();
2016-11-10 13:33:40 -08:00
} finally {
unlock();
}
};
2016-11-10 13:33:40 -08:00
/**
* Open the object (without a lock).
2017-02-03 22:47:26 -08:00
* @method
2016-11-10 13:33:40 -08:00
* @private
* @returns {Promise}
*/
AsyncObject.prototype.__open = async function open() {
2016-09-21 22:58:27 -07:00
if (this.loaded)
2016-11-10 13:33:40 -08:00
return;
2016-07-04 05:36:06 -07:00
await this.fire('preopen');
2016-09-21 22:58:27 -07:00
this.loading = true;
2016-09-21 22:58:27 -07:00
try {
await this._open();
2016-09-21 22:58:27 -07:00
} catch (e) {
2016-09-20 14:56:54 -07:00
this.loading = false;
2016-11-10 13:33:40 -08:00
this.emit('error', e);
throw e;
2016-09-21 22:58:27 -07:00
}
this.loading = false;
this.loaded = true;
await this.fire('open');
};
/**
* Close the object (recallable).
2017-02-03 22:47:26 -08:00
* @method
2016-09-23 01:05:06 -07:00
* @returns {Promise}
*/
AsyncObject.prototype.close = async function close() {
var unlock = await this._asyncLock.lock();
2016-11-10 13:33:40 -08:00
try {
return await this.__close();
2016-11-10 13:33:40 -08:00
} finally {
unlock();
}
};
2016-11-10 13:33:40 -08:00
/**
* Close the object (without a lock).
2017-02-03 22:47:26 -08:00
* @method
2016-11-10 13:33:40 -08:00
* @private
* @returns {Promise}
*/
AsyncObject.prototype.__close = async function close() {
2016-09-21 22:58:27 -07:00
if (!this.loaded)
2016-11-10 13:33:40 -08:00
return;
await this.fire('preclose');
2016-07-04 05:36:06 -07:00
2016-09-21 22:58:27 -07:00
this.closing = true;
2016-09-21 22:58:27 -07:00
try {
await this._close();
2016-09-21 22:58:27 -07:00
} catch (e) {
2016-09-20 14:56:54 -07:00
this.closing = false;
2016-11-10 13:33:40 -08:00
this.emit('error', e);
throw e;
2016-09-21 22:58:27 -07:00
}
this.closing = false;
2016-11-10 13:33:40 -08:00
this.loaded = false;
2016-09-21 22:58:27 -07:00
await this.fire('close');
};
/**
* Close the object (recallable).
* @method
2016-09-23 01:05:06 -07:00
* @returns {Promise}
*/
AsyncObject.prototype.destroy = AsyncObject.prototype.close;
/**
* Initialize the object.
* @private
2016-09-23 01:05:06 -07:00
* @returns {Promise}
*/
AsyncObject.prototype._open = function _open(callback) {
throw new Error('Abstract method.');
};
/**
* Close the object.
* @private
2016-09-23 01:05:06 -07:00
* @returns {Promise}
*/
AsyncObject.prototype._close = function _close(callback) {
throw new Error('Abstract method.');
};
/**
* Add a hook listener.
* @param {String} type
* @param {Function} handler
*/
AsyncObject.prototype.hook = function hook(type, handler) {
assert(typeof type === 'string', '`type` must be a string.');
if (!this._hooks[type])
this._hooks[type] = [];
this._hooks[type].push(handler);
};
/**
* Emit events and hooks for type.
* @method
* @param {String} type
* @param {...Object} args
* @returns {Promise}
*/
AsyncObject.prototype.fire = async function fire() {
await this.fireHook.apply(this, arguments);
this.emit.apply(this, arguments);
};
/**
* Emit an asynchronous event (hook).
* Wait for promises to resolve.
* @method
* @param {String} type
* @param {...Object} args
* @returns {Promise}
*/
AsyncObject.prototype.fireHook = async function fireHook(type) {
var i, j, listeners, args, handler;
assert(typeof type === 'string', '`type` must be a string.');
listeners = this._hooks[type];
if (!listeners || listeners.length === 0)
return;
for (i = 0; i < listeners.length; i++) {
handler = listeners[i];
switch (arguments.length) {
case 1:
await handler();
break;
case 2:
await handler(arguments[1]);
break;
case 3:
await handler(arguments[1], arguments[2]);
break;
case 4:
await handler(arguments[1], arguments[2], arguments[3]);
break;
default:
if (!args) {
args = new Array(arguments.length - 1);
for (j = 1; j < arguments.length; j++)
args[j - 1] = arguments[j];
}
await handler.apply(null, args);
break;
}
}
};
/*
* Expose
*/
module.exports = AsyncObject;