utils: rewrite async object.

This commit is contained in:
Christopher Jeffrey 2016-11-10 13:33:40 -08:00
parent d088412380
commit 06b35d4ab2
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -6,8 +6,9 @@
'use strict'; 'use strict';
var utils = require('../utils/utils'); var utils = require('./utils');
var co = require('../utils/co'); var co = require('./co');
var Locker = require('./locker');
var assert = require('assert'); var assert = require('assert');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
@ -25,10 +26,11 @@ function AsyncObject() {
EventEmitter.call(this); EventEmitter.call(this);
this._asyncLock = new Locker();
this.loading = false; this.loading = false;
this.closing = false; this.closing = false;
this.loaded = false; this.loaded = false;
this.locker = null;
} }
utils.inherits(AsyncObject, EventEmitter); utils.inherits(AsyncObject, EventEmitter);
@ -39,18 +41,23 @@ utils.inherits(AsyncObject, EventEmitter);
*/ */
AsyncObject.prototype.open = co(function* open() { AsyncObject.prototype.open = co(function* open() {
var err, unlock; var unlock = yield this._asyncLock.lock();
try {
return yield this.__open();
} finally {
unlock();
}
});
assert(!this.closing, 'Cannot open while closing.'); /**
* Open the object (without a lock).
* @private
* @returns {Promise}
*/
AsyncObject.prototype.__open = co(function* open() {
if (this.loaded) if (this.loaded)
return yield co.wait(); return;
if (this.loading)
return yield this._onOpen();
if (this.locker)
unlock = yield this.locker.lock();
this.emit('preopen'); this.emit('preopen');
@ -59,25 +66,15 @@ AsyncObject.prototype.open = co(function* open() {
try { try {
yield this._open(); yield this._open();
} catch (e) { } catch (e) {
err = e;
}
yield co.wait();
if (err) {
this.loading = false; this.loading = false;
this._error('open', err); this.emit('error', e);
if (unlock) throw e;
unlock();
throw err;
} }
this.loading = false; this.loading = false;
this.loaded = true; this.loaded = true;
this.emit('open');
if (unlock) this.emit('open');
unlock();
}); });
/** /**
@ -86,45 +83,40 @@ AsyncObject.prototype.open = co(function* open() {
*/ */
AsyncObject.prototype.close = co(function* close() { AsyncObject.prototype.close = co(function* close() {
var unlock, err; var unlock = yield this._asyncLock.lock();
try {
return yield this.__close();
} finally {
unlock();
}
});
assert(!this.loading, 'Cannot close while loading.'); /**
* Close the object (without a lock).
* @private
* @returns {Promise}
*/
AsyncObject.prototype.__close = co(function* close() {
if (!this.loaded) if (!this.loaded)
return yield co.wait(); return;
if (this.closing)
return yield this._onClose();
if (this.locker)
unlock = yield this.locker.lock();
this.emit('preclose'); this.emit('preclose');
this.closing = true; this.closing = true;
this.loaded = false;
try { try {
yield this._close(); yield this._close();
} catch (e) { } catch (e) {
err = e;
}
yield co.wait();
if (err) {
this.closing = false; this.closing = false;
this._error('close', err); this.emit('error', e);
if (unlock) throw e;
unlock();
throw err;
} }
this.closing = false; this.closing = false;
this.emit('close'); this.loaded = false;
if (unlock) this.emit('close');
unlock();
}); });
/** /**
@ -135,25 +127,6 @@ AsyncObject.prototype.close = co(function* close() {
AsyncObject.prototype.destroy = AsyncObject.prototype.close; AsyncObject.prototype.destroy = AsyncObject.prototype.close;
/**
* Emit an error for `open` or `close` listeners.
* @private
* @param {String} event
* @param {Error} err
*/
AsyncObject.prototype._error = function _error(event, err) {
var listeners = this.listeners(event);
var i;
this.removeAllListeners(event);
for (i = 0; i < listeners.length; i++)
listeners[i](err);
this.emit('error', err);
};
/** /**
* Initialize the object. * Initialize the object.
* @private * @private
@ -174,32 +147,6 @@ AsyncObject.prototype._close = function _close(callback) {
throw new Error('Abstract method.'); throw new Error('Abstract method.');
}; };
/**
* Wait for open event.
* @private
* @returns {Promise}
*/
AsyncObject.prototype._onOpen = function _onOpen() {
var self = this;
return new Promise(function(resolve, reject) {
return self.once('open', resolve);
});
};
/**
* Wait for close event.
* @private
* @returns {Promise}
*/
AsyncObject.prototype._onClose = function _onClose() {
var self = this;
return new Promise(function(resolve, reject) {
return self.once('close', resolve);
});
};
/* /*
* Expose * Expose
*/ */