diff --git a/.travis.yml b/.travis.yml index 5ec1467..9edb713 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ language: node_js node_js: - '4' - '6' - - '7' + - '8' + - '10' install: - npm i npminstall && npminstall script: diff --git a/appveyor.yml b/appveyor.yml index 2efd0fa..d3108c3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,8 @@ environment: matrix: - nodejs_version: '4' - nodejs_version: '6' - - nodejs_version: '7' + - nodejs_version: '8' + - nodejs_version: '10' install: - ps: Install-Product node $env:nodejs_version @@ -11,6 +12,6 @@ install: test_script: - node --version - npm --version - - npm run ci + - npm run test build: off diff --git a/lib/ready.js b/lib/ready.js index e85dfbe..f2b4487 100644 --- a/lib/ready.js +++ b/lib/ready.js @@ -5,6 +5,8 @@ const once = require('once'); const ready = require('get-ready'); const uuid = require('uuid'); const debug = require('debug')('ready-callback'); +const WILL_READY_CALLBACKS = Symbol('willReadyCallbacks'); +const TRIGGER_WILL_READY = Symbol('triggerWillReady'); const defaults = { timeout: 10000, @@ -29,12 +31,13 @@ class Ready extends EventEmitter { this.opt = opt || {}; this.isError = false; this.cache = new Map(); + this[WILL_READY_CALLBACKS] = []; setImmediate(() => { // fire callback directly when no registered ready callback if (this.cache.size === 0) { debug('Fire callback directly'); - this.ready(true); + this[TRIGGER_WILL_READY](); } }); } @@ -52,6 +55,7 @@ class Ready extends EventEmitter { // delegate API to object obj.ready = this.ready.bind(this); obj.readyCallback = this.readyCallback.bind(this); + obj.willReady = this.willReady.bind(this); // only ready once with error this.once('error', err => obj.ready(err)); @@ -121,11 +125,27 @@ class Ready extends EventEmitter { if (this.cache.size === 0) { debug('[%s] Fire callback async', id); - this.ready(true); + this[TRIGGER_WILL_READY](); } return this; } + willReady(fn) { + this[WILL_READY_CALLBACKS].push(fn); + } + + [TRIGGER_WILL_READY]() { + return Promise.all(this[WILL_READY_CALLBACKS] + .map(fn => { + try { + return fn(); + } catch (e) { + return Promise.reject(e); + } + })) + .then(() => this.ready(true)) + .catch(e => this.ready(e)); + } } // Use ready-callback with options diff --git a/package.json b/package.json index 0a827fe..ca9d01d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "autod": "^2.7.1", - "egg-bin": "^2.0.2", + "egg-bin": "^1.0.0", "egg-ci": "^1.1.0", "eslint": "^3.15.0", "eslint-config-egg": "^3.2.0", @@ -45,6 +45,6 @@ "node": ">=4.0.0" }, "ci": { - "version": "4, 6, 7" + "version": "4, 6, 8, 10" } } diff --git a/test/ready.test.js b/test/ready.test.js index 5ebea0a..4e755f8 100644 --- a/test/ready.test.js +++ b/test/ready.test.js @@ -462,4 +462,42 @@ describe('Ready', function() { assert(ready.obj === obj1); assert(ready.obj !== obj2); }); + + describe('willReady', function() { + it('should fire willRead callbacks before ready', function(done) { + const obj = new EventEmitter(); + const ready = new Ready(); + ready.mixin(obj); + const endA = obj.readyCallback('a'); + const spyWillReady = spy(); + obj.willReady(spyWillReady); + setTimeout(endA, 1); + setTimeout(function() { + assert.strictEqual(spyWillReady.callCount, 1); + done(); + }, 10); + }); + + describe('willReady failed', function() { + it('should ready error', function(done) { + const obj = new EventEmitter(); + const ready = new Ready(); + ready.mixin(obj); + const endA = obj.readyCallback('a'); + const willReadyThrows = function() { + throw new Error('mock error'); + }; + obj.willReady(willReadyThrows); + let err; + obj.ready(function(e) { + err = e; + }); + setTimeout(endA, 1); + setTimeout(function() { + assert.strictEqual(err && err.message, 'mock error'); + done(); + }, 10); + }); + }); + }); });