diff --git a/CHANGE.md b/CHANGE.md index 2189b4d..f5bc3f0 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,6 +1,13 @@ PromiseClass change log ==================== +## ver 0.9.5 (2015-11-9) + +add: + +1. Support define generator method: `PromiseClass.create({*method(){ }});` +2. Promise callback can use generator function: `.then(function*(){ })` + ## ver 0.9.4 (2015-11-8) change: diff --git a/README.md b/README.md index a48b51e..6b0817d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Features 5. Support addMethod & addPropertie after class define 6. Support generator & ES7 await 7. Support async callback & promise & generator mix use +8. Supoort define generator method, and promise callback support generator function Quick start ====================== @@ -38,26 +39,31 @@ Try it: }, syncMethod(n){ - console.log('syncMethod called'); + console.log('syncMethod called : '+n); return 'syncMethod return : '+n; }, asyncMethod1(n, done){ - console.log('asyncMethod1 called'); + console.log('asyncMethod1 called : '+n); this.asyncMethod2(n).then(function(){ - console.log('asyncMethod1 done'); + console.log('asyncMethod1 done : '+n); done(null, 'asyncMethod1 return : '+n); }).catch(done); }, asyncMethod2(n, done){ - console.log('asyncMethod2 called'); + console.log('asyncMethod2 called : '+n); setTimeout(function(){ - console.log('asyncMethod2 done'); + console.log('asyncMethod2 done : '+n); done(null, 'asyncMethod2 return : '+n); }, n); }, + *generatorMethod1(n){ + console.log('generatorMethod1 called : '+n); + return yield this.asyncMethod2(n); + }, + propertie1: 1, propertie2: 2 @@ -65,12 +71,17 @@ Try it: // add async method App.addMethod('asyncMethod3', function(n, done){ - console.log('asyncMethod3 called'); + console.log('asyncMethod3 called : '+n); setTimeout(function(){ - console.log('asyncMethod3 done'); + console.log('asyncMethod3 done : '+n); done(null, 'asyncMethod3 return : '+ n); }, n); }); + // add generator method + App.addMethod('generatorMethod2', function*(n){ + console.log('generatorMethod2 called : '+n); + return yield this.asyncMethod2(n); + }); // add propertie App.addPropertie('propertie3', 3); @@ -81,6 +92,7 @@ Try it: var app = new App('async'); app.syncMethod(0, function(error, ret){ console.log(ret); + console.log('--------------------------'); app.asyncMethod1(1, function(error, ret){ console.log(ret); console.log('================ async end ================'); @@ -98,10 +110,13 @@ Try it: var app = new App('promise'); app.syncMethod(0).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod1(1).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod2(2).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod3(3).then(function(ret){ console.log(ret); console.log('================ promise end ================'); @@ -120,17 +135,27 @@ Try it: yield app.syncMethod(0).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod1(1).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod2(2).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod3(3).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); + yield app.generatorMethod1(4).then(function*(ret){ + console.log(ret); + return yield this.asyncMethod1(5); + }).then(function(ret){ + console.log(ret); + }); }).then(function(){ - console.log('done:'); + console.log('done!'); console.log('================ generator end ================'); console.log(''); deferred.resolve(); @@ -146,6 +171,7 @@ Try it: + License ================ diff --git a/example/demo.js b/example/demo.js index 521b077..9a2c201 100644 --- a/example/demo.js +++ b/example/demo.js @@ -8,26 +8,31 @@ var App = PromiseClass.create({ }, syncMethod(n){ - console.log('syncMethod called'); + console.log('syncMethod called : '+n); return 'syncMethod return : '+n; }, asyncMethod1(n, done){ - console.log('asyncMethod1 called'); + console.log('asyncMethod1 called : '+n); this.asyncMethod2(n).then(function(){ - console.log('asyncMethod1 done'); + console.log('asyncMethod1 done : '+n); done(null, 'asyncMethod1 return : '+n); }).catch(done); }, asyncMethod2(n, done){ - console.log('asyncMethod2 called'); + console.log('asyncMethod2 called : '+n); setTimeout(function(){ - console.log('asyncMethod2 done'); + console.log('asyncMethod2 done : '+n); done(null, 'asyncMethod2 return : '+n); }, n); }, + *generatorMethod1(n){ + console.log('generatorMethod1 called : '+n); + return yield this.asyncMethod2(n); + }, + propertie1: 1, propertie2: 2 @@ -35,12 +40,17 @@ var App = PromiseClass.create({ // add async method App.addMethod('asyncMethod3', function(n, done){ - console.log('asyncMethod3 called'); + console.log('asyncMethod3 called : '+n); setTimeout(function(){ - console.log('asyncMethod3 done'); + console.log('asyncMethod3 done : '+n); done(null, 'asyncMethod3 return : '+ n); }, n); }); +// add generator method +App.addMethod('generatorMethod2', function*(n){ + console.log('generatorMethod2 called : '+n); + return yield this.asyncMethod2(n); +}); // add propertie App.addPropertie('propertie3', 3); @@ -51,6 +61,7 @@ function async(){ var app = new App('async'); app.syncMethod(0, function(error, ret){ console.log(ret); + console.log('--------------------------'); app.asyncMethod1(1, function(error, ret){ console.log(ret); console.log('================ async end ================'); @@ -68,10 +79,13 @@ function promise(){ var app = new App('promise'); app.syncMethod(0).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod1(1).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod2(2).then(function(ret){ console.log(ret); + console.log('--------------------------'); }).asyncMethod3(3).then(function(ret){ console.log(ret); console.log('================ promise end ================'); @@ -90,15 +104,25 @@ function generator(){ yield app.syncMethod(0).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod1(1).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod2(2).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); yield app.asyncMethod3(3).then(function(ret){ console.log(ret); }); + console.log('--------------------------'); + yield app.generatorMethod1(4).then(function*(ret){ + console.log(ret); + return yield this.asyncMethod1(5); + }).then(function(ret){ + console.log(ret); + }); }).then(function(){ console.log('done!'); console.log('================ generator end ================'); diff --git a/lib/promiseclass.js b/lib/promiseclass.js index 997071b..b34c51f 100644 --- a/lib/promiseclass.js +++ b/lib/promiseclass.js @@ -3,6 +3,7 @@ * Copyright (c) 2015, Yanis Wang * MIT Licensed */ +const co = require('co'); const PromiseClass = { create: function(classDefines){ @@ -22,9 +23,22 @@ const PromiseClass = { // hook Promise then function let PromisePrototype = ChainPromise.prototype; let rawThen = PromisePrototype.then; - PromisePrototype.then = function(){ + PromisePrototype.then = function(onResolve, onReject){ let self = this; - let promise = rawThen.apply(self, arguments); + if(isGeneratorFunction(onResolve)){ + // support then generator + let rawOnResolve = onResolve; + onResolve = function(){ + return co(rawOnResolve.apply(self, arguments)); + }; + } + if(isGeneratorFunction(onReject)){ + let rawOnReject = onReject; + onReject = function(){ + return co(rawOnReject.apply(self, arguments)); + }; + } + let promise = rawThen.call(self, onResolve, onReject); promise._context = self._context; return promise; }; @@ -42,8 +56,17 @@ const PromiseClass = { callback = args.pop(); } function done(error, ret){ - callback && callback(error, ret); - error ? deferred.reject(error) : deferred.resolve(ret); + if(callback){ + let newRet = callback.call(self, error, ret); + if(isGenerator(newRet)){ + // support callback generator + newRet = co(newRet); + } + deferred.resolve(newRet); + } + else{ + error ? deferred.reject(error) : deferred.resolve(ret); + } } // async modem hook done if(isAsync){ @@ -53,7 +76,15 @@ const PromiseClass = { let ret = fn.apply(self, args); // sync mode if(isAsync === false){ - done(null, ret); + if(ret !== undefined && isGenerator(ret)){ + // support method generator + co(ret).then(function(ret){ + done(null, ret); + }).catch(done); + } + else{ + done(null, ret); + } } } catch(error){ @@ -98,11 +129,26 @@ const PromiseClass = { function isCallback(arg){ if(typeof arg === 'function'){ var str = String(arg); - if(/^function(\s+(done|callback|cb))\s*\(/.test(str) || - /^function(\s+\w+)?\s*\(\s*(error|err|e)\s*(,|\))/i.test(str)){ + if(/^function\*?(\s+(done|callback|cb))\s*\(/.test(str) || + /^function\*?(\s+\w+)?\s*\(\s*(error|err|e)\s*(,|\))/i.test(str)){ return true; } } return false; } + +// check is generator function +function isGeneratorFunction(obj) { + var constructor = obj && obj.constructor; + if (constructor && ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName)) { + return true; + } + return false; +} + +// check is generator +function isGenerator(obj) { + return obj && 'function' == typeof obj.next && 'function' == typeof obj.throw; +} + module.exports = PromiseClass; diff --git a/package.json b/package.json index 71da880..1329d84 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "promiseclass", - "version": "0.9.4", + "version": "0.9.5", "description": "Create Promise chain Class", "main": "./index", - "dependencies": {}, + "dependencies": { + "co": "4.6.0" + }, "devDependencies": { - "co": "4.6.0", "expect.js": "0.3.1", "grunt": "0.4.1", "grunt-cli": "0.1.6", @@ -15,7 +16,8 @@ "grunt-exec": "0.4.0", "istanbul": "0.3.22", "istanbul-coveralls": "1.0.3", - "mocha": "2.3.3" + "mocha": "2.3.3", + "mocha-generators": "1.2.0" }, "scripts": { "test": "mocha" diff --git a/test/case.spec.js b/test/case.spec.js index 05472ea..a1c6433 100644 --- a/test/case.spec.js +++ b/test/case.spec.js @@ -4,154 +4,80 @@ */ var expect = require("expect.js"); -var co = require("co"); +require('mocha-generators').install(); var PromiseClass = require('../'); -describe('PromiseClass Generator test : ', function(){ - - it('should run all chain method', function(done){ - co(function*(){ - var isInited = false; - var isMethod2Called = false; - var App = PromiseClass.create({ - constructor(){ - isInited = true; - }, - method1(n){ - return n; - }, - method2(n, done){ - setTimeout(function(){ - isMethod2Called = true; - done(null, n); - }, 10); - } - }); - var app = new App(); - expect(isInited).to.be(true); - yield app.method2(111).then(function(ret){ - expect(ret).to.be(111); - }).method1(222).then(function(ret){ - expect(ret).to.be(222); - }); - expect(isMethod2Called).to.be(true); - }).then(function(){ - done(); - }).catch(done); - }); +describe('PromiseClass other test : ', function(){ - it('should catch sync method error', function(done){ - co(function*(){ - var App = PromiseClass.create({ - method(){ - throw new Error('test'); - } - }); - var app = new App(); - yield app.method(); - }).catch(function(){ - done(); + it('all kind of callback should work well', function*(){ + var callbackCount = 0; + var App = PromiseClass.create({ + method(n, done){ + done(null, n); + } }); - }); - - it('should catch async method error', function(done){ - co(function*(){ - var App = PromiseClass.create({ - method(n, done){ - setTimeout(function(){ - done('error'); - }, 10); - } - }); - var app = new App(); - yield app.method(111); - }).catch(function(){ - done(); + var app = new App(); + yield app.method(1, function(error){ + !error && callbackCount ++; }); + yield app.method(2, function(err){ + !err && callbackCount ++; + }); + yield app.method(3, function(e){ + !e && callbackCount ++; + }); + yield app.method(4, function done(){ + callbackCount ++; + }); + yield app.method(5, function callback(){ + callbackCount ++; + }); + yield app.method(6, function cb(){ + callbackCount ++; + }); + expect(callbackCount).to.be(6); }); -}); - -describe('PromiseClass other test : ', function(){ - - it('all kind of callback should work well', function(done){ - co(function*(){ - var callbackCount = 0; - var App = PromiseClass.create({ - method(n, done){ - done(null, n); - } - }); - var app = new App(); - yield app.method(1, function(error){ - !error && callbackCount ++; - }); - yield app.method(2, function(err){ - !err && callbackCount ++; - }); - yield app.method(3, function(e){ - !e && callbackCount ++; - }); - yield app.method(4, function done(){ - callbackCount ++; - }); - yield app.method(5, function callback(){ - callbackCount ++; - }); - yield app.method(6, function cb(){ - callbackCount ++; - }); - expect(callbackCount).to.be(6); - done(); - }).catch(done); - }); - - it('type of the first argument is function should work well', function(done){ - co(function*(){ - var App = PromiseClass.create({ - method(n, done){ - done(); - } - }); - var callbackCount = 0; - var app = new App(); - yield app.method(function(){ - throw new Error('test'); - }); - yield app.method(function(){ - throw new Error('test'); - }, function(err){ - !err && callbackCount ++; - }); - yield app.method(function(arg){ - !arg; - throw new Error('test'); - }, function done(){ - callbackCount ++; - }); - expect(callbackCount).to.be(2); - done(); - }).catch(done); + it('type of the first argument is function should work well', function*(){ + var App = PromiseClass.create({ + method(n, done){ + done(); + } + }); + var callbackCount = 0; + var app = new App(); + yield app.method(function(){ + throw new Error('test'); + }); + yield app.method(function(){ + throw new Error('test'); + }, function(err){ + !err && callbackCount ++; + }); + yield app.method(function(arg){ + !arg; + throw new Error('test'); + }, function done(){ + callbackCount ++; + }); + expect(callbackCount).to.be(2); }); - it('method should return PromiseClass object', function(done){ - co(function*(){ - var App1 = PromiseClass.create({ - method1(){ - return new App2(); - } - }); - var App2 = PromiseClass.create({ - method2(){ - return 2; - } - }); - var app1 = new App1(); - var app2 = yield app1.method1(); - expect(app2 instanceof App2).to.be(true); - done(); - }).catch(done); + it('method should return PromiseClass object', function*(){ + var App1 = PromiseClass.create({ + method1(){ + return new App2(); + } + }); + var App2 = PromiseClass.create({ + method2(){ + return 2; + } + }); + var app1 = new App1(); + var app2 = yield app1.method1(); + expect(app2 instanceof App2).to.be(true); }); }); diff --git a/test/promiseclass.spec.js b/test/promiseclass.spec.js index 6416848..14858a5 100644 --- a/test/promiseclass.spec.js +++ b/test/promiseclass.spec.js @@ -4,12 +4,13 @@ */ var expect = require("expect.js"); +require('mocha-generators').install(); var PromiseClass = require('../'); describe('PromiseClass Constructor test : ', function(){ - it('should define constructor and been called', function(done){ + it('should define constructor and been called', function(){ var isInited = false; var App = PromiseClass.create({ constructor(){ @@ -19,7 +20,6 @@ describe('PromiseClass Constructor test : ', function(){ var app = new App(); expect(app).to.be.an('object'); expect(isInited).to.be(true); - done(); }); it('should return promise when no constructor', function(done){ @@ -37,7 +37,7 @@ describe('PromiseClass Constructor test : ', function(){ describe('PromiseClass Propertie test : ', function(){ - it('should define properties', function(done){ + it('should define properties', function(){ var App = PromiseClass.create({ propertie1: 1, propertie2: 2, @@ -57,12 +57,10 @@ describe('PromiseClass Propertie test : ', function(){ var app = new App(); expect(app.propertie1).to.be(3); expect(app.propertie2).to.be(4); - app.checkProperties().then(function(){ - done(); - }).catch(done); + app.checkProperties(); }); - it('should add properties', function(done){ + it('should add properties', function(){ var App = PromiseClass.create({ constructor(){ var self = this; @@ -78,9 +76,7 @@ describe('PromiseClass Propertie test : ', function(){ var app = new App(); App.addPropertie('propertie1', 1); App.addPropertie('propertie2', 2); - app.checkProperties().then(function(){ - done(); - }).catch(done); + app.checkProperties(); }); }); @@ -100,11 +96,12 @@ describe('PromiseClass Method test : ', function(){ } }); var app = new App(); - app.method('111', function(error, ret){ - expect(ret).to.be('111'); + app.method(111, function(error, ret){ + expect(ret).to.be(111); + return 222; }).then(function(ret){ expect(isCalled).to.be(true); - expect(ret).to.be('111'); + expect(ret).to.be(222); done(); }).catch(done); }); @@ -122,11 +119,12 @@ describe('PromiseClass Method test : ', function(){ isCalled = true; return str; }); - app.method('222', function(error, ret){ - expect(ret).to.be('222'); + app.method(222, function(error, ret){ + expect(ret).to.be(222); + return 333; }).then(function(ret){ expect(isCalled).to.be(true); - expect(ret).to.be('222'); + expect(ret).to.be(333); done(); }).catch(done); }); @@ -146,11 +144,12 @@ describe('PromiseClass Method test : ', function(){ } }); var app = new App(); - app.method('222', function(error, ret){ - expect(ret).to.be('222'); + app.method(222, function(error, ret){ + expect(ret).to.be(222); + return 333; }).then(function(ret){ expect(isCalled).to.be(true); - expect(ret).to.be('222'); + expect(ret).to.be(333); done(); }).catch(done); }); @@ -170,11 +169,12 @@ describe('PromiseClass Method test : ', function(){ done(null, str); }, 10); }); - app.method('222', function(error, ret){ - expect(ret).to.be('222'); + app.method(222, function(error, ret){ + expect(ret).to.be(222); + return 333; }).then(function(ret){ expect(isCalled).to.be(true); - expect(ret).to.be('222'); + expect(ret).to.be(333); done(); }).catch(done); }); @@ -267,3 +267,136 @@ describe('PromiseClass instanceof test : ', function(){ }); }); + +describe('PromiseClass Generator test : ', function(){ + + it('should run all chain method', function*(){ + var isInited = false; + var isMethod2Called = false; + var App = PromiseClass.create({ + constructor(){ + isInited = true; + }, + method1(n){ + return n; + }, + method2(n, done){ + setTimeout(function(){ + isMethod2Called = true; + done(null, n); + }, 10); + } + }); + var app = new App(); + expect(isInited).to.be(true); + yield app.method2(111).then(function(ret){ + expect(ret).to.be(111); + }).method1(222).then(function(ret){ + expect(ret).to.be(222); + }); + expect(isMethod2Called).to.be(true); + }); + + it('should catch sync method error', function(done){ + var App = PromiseClass.create({ + method(){ + throw new Error('test'); + } + }); + var app = new App(); + app.method().catch(function(){ + done(); + }); + }); + + it('should catch async method error', function(done){ + var App = PromiseClass.create({ + method(n, done){ + setTimeout(function(){ + done('error'); + }, 10); + } + }); + var app = new App(); + app.method(111).catch(function(){ + done(); + }); + }); + + it('method should define generator method', function*(){ + var App = PromiseClass.create({ + *method(n){ + this.aaa=n; + return yield sleep(n); + } + }); + var app = new App(); + yield app.method(11, function(error, ret){ + expect(this.aaa).to.be(11); + expect(ret).to.be(11); + }).method(22).then(function(ret){ + expect(ret).to.be(22); + }); + }); + + it('should add generator method', function*(){ + var App = PromiseClass.create({ + }); + App.addMethod('method', function*(n){ + this.aaa=n; + return yield sleep(n); + }); + var app = new App(); + yield app.method(11, function(error, ret){ + expect(this.aaa).to.be(11); + expect(ret).to.be(11); + }).method(22).then(function(ret){ + expect(ret).to.be(22); + }); + }); + + it('async callback and promise callback should use generator', function*(){ + var App = PromiseClass.create({ + method(n){ + return n; + } + }); + var app = new App(); + yield app.method(11, function*(error, ret){ + // check async callback + expect(ret).to.be(11); + return yield sleep(22); + }).then(function*(ret){ + // check promise callback + expect(ret).to.be(22); + return yield sleep(33); + }).then(function(ret){ + // check promise onReject callback + expect(ret).to.be(33); + throw new Error('test'); + }).then(function(){ + }, function*(error){ + expect(error.message).to.contain('not defined'); + return yield sleep(44); + }).then(function(ret){ + // check promise catch callback + expect(ret).to.be(44); + throw new Error('test'); + }).catch(function*(error){ + expect(error.message).to.contain('not defined'); + return yield sleep(55); + }).then(function(ret){ + expect(ret).to.be(55); + }); + }); + +}); + +// promise sleep +function sleep(ms){ + return new Promise(function(resolve){ + setTimeout(function(){ + resolve(ms); + }, ms); + }); +}