├── .autod.conf.js ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── History.md ├── LICENSE ├── README.md ├── appveyor.yml ├── index.js ├── package.json └── test └── index.test.js /.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | write: true, 5 | prefix: '^', 6 | devprefix: '^', 7 | exclude: [ 8 | ], 9 | devdep: [ 10 | 'autod', 11 | 'egg-bin', 12 | 'egg-ci', 13 | 'eslint', 14 | 'eslint-config-egg', 15 | ], 16 | keep: [ 17 | ], 18 | semver: [ 19 | 'egg-bin@1', 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "egg" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '4' 5 | - '6' 6 | - '7' 7 | install: 8 | - npm i npminstall && npminstall 9 | script: 10 | - npm run ci 11 | after_script: 12 | - npminstall codecov && codecov 13 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.0.1 / 2017-02-09 3 | ================== 4 | 5 | * fix: it should reject error when ready return promise (#3) 6 | 7 | 2.0.0 / 2017-02-08 8 | ================== 9 | 10 | * feat: [BREAKING_CHANGE] reimplement get-ready (#2) 11 | * fix typo on readme 12 | 13 | 1.0.0 / 2015-09-29 14 | ================== 15 | 16 | * chore: use eslint and es6 17 | * test: add test with co 18 | * travis: test on node(1,2,3,4) 19 | * feat: support promise 20 | * fork from supershabam/ready 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ian Matthew Hansen 4 | Copyright (c) 2015 node-modules and other contributors. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # get-ready 2 | ===== 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![build status][travis-image]][travis-url] 6 | [![Test coverage][codecov-image]][codecov-url] 7 | [![David deps][david-image]][david-url] 8 | [![npm download][download-image]][download-url] 9 | 10 | [npm-image]: https://img.shields.io/npm/v/get-ready.svg?style=flat-square 11 | [npm-url]: https://npmjs.org/package/get-ready 12 | [travis-image]: https://img.shields.io/travis/node-modules/ready.svg?style=flat-square 13 | [travis-url]: https://travis-ci.org/node-modules/ready 14 | [codecov-image]: https://codecov.io/github/node-modules/ready/coverage.svg?branch=master 15 | [codecov-url]: https://codecov.io/github/node-modules/ready?branch=master 16 | [david-image]: https://img.shields.io/david/node-modules/ready.svg?style=flat-square 17 | [david-url]: https://david-dm.org/node-modules/ready 18 | [download-image]: https://img.shields.io/npm/dm/get-ready.svg?style=flat-square 19 | [download-url]: https://npmjs.org/package/get-ready 20 | 21 | **Fork from [supershabam/ready](https://github.com/supershabam/ready)** 22 | 23 | NodeJS mixin to add one-time ready event 24 | 25 | ## Usage 26 | 27 | Using `ready` or `ready.mixin` to add `ready` method to the given object. 28 | 29 | ```js 30 | const ready = require('get-ready'); 31 | const obj = {}; 32 | ready.mixin(obj); 33 | 34 | // register a callback 35 | obj.ready(() => console.log('ready')); 36 | 37 | // mark ready 38 | obj.ready(true); 39 | ``` 40 | 41 | ### Register 42 | 43 | Register a callback to the callback stack, it will be called when mark as ready, see example above. 44 | 45 | If the callback is undefined, register will return a promise. 46 | 47 | ```js 48 | obj.ready().then(() => console.log('ready')); 49 | obj.ready(true); 50 | ``` 51 | 52 | If it has been ready, the callback will be called immediately. 53 | 54 | ```js 55 | // already ready 56 | obj.ready(true); 57 | obj.ready().then(() => console.log('ready')); 58 | ``` 59 | 60 | **Warning: the callback is called after nextTick** 61 | 62 | ### Emit 63 | 64 | Mark it as ready, you can simply using `.ready(true)`. 65 | 66 | You can also mark it not ready. 67 | 68 | ```js 69 | obj.ready(true); 70 | // call immediately 71 | obj.ready(() => console.log('ready')); 72 | 73 | obj.ready(false); 74 | obj.ready(() => throw 'don\'t run'); 75 | ``` 76 | 77 | When exception throws, you can pass an error object, then the callback will receive it as the first argument. 78 | 79 | ```js 80 | obj.ready(err => console.log(err)); 81 | obj.ready(new Error('err')); 82 | ``` 83 | 84 | 85 | ## License 86 | 87 | [MIT](LICENSE) 88 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '4' 4 | - nodejs_version: '6' 5 | - nodejs_version: '7' 6 | 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm i npminstall && node_modules\.bin\npminstall 10 | 11 | test_script: 12 | - node --version 13 | - npm --version 14 | - npm run ci 15 | 16 | build: off 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const is = require('is-type-of'); 4 | 5 | const IS_READY = Symbol('isReady'); 6 | const READY_CALLBACKS = Symbol('readyCallbacks'); 7 | const READY_ARG = Symbol('readyArg'); 8 | 9 | class Ready { 10 | 11 | constructor() { 12 | this[IS_READY] = false; 13 | this[READY_CALLBACKS] = []; 14 | } 15 | 16 | ready(flagOrFunction) { 17 | // register a callback 18 | if (flagOrFunction === undefined || is.function(flagOrFunction)) { 19 | return this.register(flagOrFunction); 20 | } 21 | // emit callbacks 22 | this.emit(flagOrFunction); 23 | } 24 | 25 | 26 | /** 27 | * Register a callback to the callback stack, it will be called when emit. 28 | * It will return promise when no argument passing. 29 | * @param {Function|Undefined} func - a callback 30 | * @return {Undefined|Promise} promise 31 | */ 32 | register(func) { 33 | // support `this.ready().then(onready);` and `yield this.ready()`; 34 | if (!func) { 35 | return new Promise((resolve, reject) => { 36 | function func(err) { 37 | if (err) { 38 | reject(err); 39 | } else { 40 | resolve(); 41 | } 42 | } 43 | if (this[IS_READY]) { 44 | return func(this[READY_ARG]); 45 | } 46 | this[READY_CALLBACKS].push(func); 47 | }); 48 | } 49 | 50 | // this.ready(fn) 51 | if (this[IS_READY]) { 52 | func(this[READY_ARG]); 53 | } else { 54 | this[READY_CALLBACKS].push(func); 55 | } 56 | } 57 | 58 | /** 59 | * Call the callbacks that has been registerd, and clean the callback stack. 60 | * If the flag is not false, it will be marked as ready. Then the callbacks will be called immediatly when register. 61 | * @param {Boolean|Error} flag - Set a flag whether it had been ready. If the flag is an error, it's also ready, but the callback will be called with argument `error` 62 | */ 63 | emit(flag) { 64 | // this.ready(true); 65 | // this.ready(false); 66 | // this.ready(err); 67 | this[IS_READY] = flag !== false; 68 | this[READY_ARG] = flag instanceof Error ? flag : undefined; 69 | // this.ready(true) 70 | if (this[IS_READY]) { 71 | this[READY_CALLBACKS] 72 | .splice(0, Infinity) 73 | .forEach(callback => process.nextTick(() => callback(this[READY_ARG]))); 74 | } 75 | } 76 | 77 | /** 78 | * @param {Object} obj - an object that be mixed 79 | */ 80 | static mixin(obj) { 81 | if (!obj) return; 82 | const ready = new Ready(); 83 | // delegate method 84 | obj.ready = flagOrFunction => ready.ready(flagOrFunction); 85 | } 86 | } 87 | 88 | function mixin(object) { 89 | Ready.mixin(object); 90 | } 91 | 92 | module.exports = mixin; 93 | module.exports.mixin = mixin; 94 | module.exports.Ready = Ready; 95 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-ready", 3 | "version": "2.0.1", 4 | "description": "mixin to add one-time ready event callback handler", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "dependencies": { 10 | "is-type-of": "^1.0.0" 11 | }, 12 | "engines": { 13 | "node": ">= 4.0.0" 14 | }, 15 | "devDependencies": { 16 | "autod": "^2.7.1", 17 | "egg-bin": "^1.10.3", 18 | "egg-ci": "^1.5.0", 19 | "eslint": "^3.17.1", 20 | "eslint-config-egg": "^3.2.0" 21 | }, 22 | "scripts": { 23 | "autod": "autod", 24 | "lint": "eslint .", 25 | "test": "npm run lint && egg-bin test", 26 | "cov": "egg-bin cov", 27 | "ci": "npm run lint && npm run cov" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/node-modules/ready" 32 | }, 33 | "keywords": [ 34 | "ready", 35 | "once", 36 | "event" 37 | ], 38 | "author": "fengmk2 (http://fengmk2.com)", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/node-modules/ready/issues" 42 | }, 43 | "ci": { 44 | "version": "4, 6, 7" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const ready = require('../'); 5 | 6 | class SomeClass { 7 | constructor() { 8 | this.property = 'value'; 9 | ready.mixin(this); 10 | } 11 | 12 | method() { 13 | return 'method'; 14 | } 15 | } 16 | 17 | describe('exports', () => { 18 | it('should exports mixin', () => { 19 | assert(ready.mixin); 20 | assert(ready.mixin === ready); 21 | }); 22 | 23 | it('should exports Ready', () => { 24 | assert(ready.Ready); 25 | }); 26 | }); 27 | 28 | describe('mixin', () => { 29 | it('should not throw when mixin undefined', () => { 30 | ready(); 31 | }); 32 | }); 33 | 34 | describe('inherits', () => { 35 | 36 | it('should have Ready properties', () => { 37 | const someClass = new SomeClass(); 38 | assert('ready' in someClass); 39 | }); 40 | 41 | it('should be separate from other instances', function* () { 42 | const someClass = new SomeClass(); 43 | const anotherClass = new SomeClass(); 44 | let someCallCount = 0; 45 | let anotherCallCount = 0; 46 | anotherClass.ready(() => { anotherCallCount++; }); 47 | someClass.ready(() => { someCallCount++; }); 48 | someClass.ready(() => { someCallCount++; }); 49 | someClass.ready(true); 50 | anotherClass.ready(true); 51 | yield nextTick(); 52 | assert(someCallCount === 2); 53 | assert(anotherCallCount === 1); 54 | }); 55 | 56 | it('should ready(obj) directly work', () => { 57 | const foo = {}; 58 | assert(!('ready' in foo)); 59 | ready(foo); 60 | assert(typeof foo.ready === 'function'); 61 | }); 62 | }); 63 | 64 | describe('ready', () => { 65 | 66 | it('should execute and dequeue callbacks', function* () { 67 | const someClass = new SomeClass(); 68 | const arr = []; 69 | someClass.ready(() => arr.push(1)); 70 | someClass.ready(() => arr.push(2)); 71 | someClass.ready(true); 72 | yield nextTick(); 73 | assert.deepEqual(arr, [ 1, 2 ]); 74 | 75 | someClass.ready(true); 76 | yield nextTick(); 77 | assert.deepEqual(arr, [ 1, 2 ]); 78 | }); 79 | 80 | it('should immediatly call callback when already ready', function(done) { 81 | const someClass = new SomeClass(); 82 | someClass.ready(true); 83 | someClass.ready(done); 84 | }); 85 | 86 | it('should not call when ready set to false', function(done) { 87 | const someClass = new SomeClass(); 88 | someClass.ready(true); 89 | someClass.ready(false); 90 | someClass.ready(done => done('should not execute because it is not ready')); 91 | setTimeout(() => { 92 | done(); 93 | }, 10); 94 | }); 95 | 96 | it('should ready when using other type', done => { 97 | const someClass = new SomeClass(); 98 | someClass.ready(done); 99 | someClass.ready(0); 100 | }); 101 | }); 102 | 103 | describe('promise', () => { 104 | it('should resolve after ready', function(done) { 105 | const someClass = new SomeClass(); 106 | someClass.ready().then(() => { 107 | someClass.ready().then(done); 108 | }); 109 | someClass.ready(true); 110 | }); 111 | }); 112 | 113 | describe('generator', () => { 114 | it('should work with co', function* () { 115 | const someClass = new SomeClass(); 116 | setTimeout(() => { 117 | someClass.ready(true); 118 | }, 100); 119 | yield someClass.ready(); 120 | }); 121 | }); 122 | 123 | describe('error', () => { 124 | it('should get error in callback', done => { 125 | const someClass = new SomeClass(); 126 | someClass.ready(err => { 127 | assert(err); 128 | assert(err.message === 'error'); 129 | done(); 130 | }); 131 | someClass.ready(new Error('error')); 132 | }); 133 | 134 | it('should get error in promise', done => { 135 | const someClass = new SomeClass(); 136 | someClass.ready().catch(err => { 137 | assert(err); 138 | assert(err.message === 'error'); 139 | done(); 140 | }); 141 | someClass.ready(new Error('error')); 142 | }); 143 | 144 | it('should get error after ready in callback', done => { 145 | const someClass = new SomeClass(); 146 | someClass.ready(new Error('error')); 147 | someClass.ready(err => { 148 | assert(err); 149 | assert(err.message === 'error'); 150 | done(); 151 | }); 152 | }); 153 | 154 | it('should get error after ready in promise', done => { 155 | const someClass = new SomeClass(); 156 | someClass.ready(new Error('error')); 157 | someClass.ready().catch(err => { 158 | assert(err); 159 | assert(err.message === 'error'); 160 | done(); 161 | }); 162 | }); 163 | }); 164 | 165 | function nextTick() { 166 | return done => process.nextTick(done); 167 | } 168 | --------------------------------------------------------------------------------