-
Notifications
You must be signed in to change notification settings - Fork 0
/
promiseclass.js
154 lines (149 loc) · 5.31 KB
/
promiseclass.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
'use strict';
/**
* Copyright (c) 2015, Yanis Wang <yanis.wang@gmail.com>
* MIT Licensed
*/
const co = require('co');
const PromiseClass = {
create: function(classDefines){
var constructor;
if(classDefines.hasOwnProperty('constructor') === true){
constructor = classDefines['constructor'];
delete classDefines['constructor'];
}
let newPromiseClass = function PromiseClass(){
if(constructor !== undefined){
constructor.apply(this, arguments);
}
};
let classPrototype = newPromiseClass.prototype;
// define private Promise
let ChainPromise = class ChainPromise extends Promise {};
// hook Promise then function
let PromisePrototype = ChainPromise.prototype;
let rawThen = PromisePrototype.then;
PromisePrototype.then = function(onResolve, onReject){
let self = this;
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;
};
// add promise+chain method
newPromiseClass.addMethod = function(name, fn){
function wrapFunction(){
let self = this;
let args = Array.prototype.slice.call(arguments, 0);
let isAsync = /(\(|,)\s*(done|callback|cb)\s*\)\s*\{/i.test(fn);
let deferred = ChainPromise.defer();
let promise = deferred.promise;
let callback;
if(isCallback(args[args.length - 1])){
// async mode
callback = args.pop();
}
function done(error, 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){
args.push(done);
}
try{
let ret = fn.apply(self, args);
// sync mode
if(isAsync === false){
if(ret !== undefined && isGenerator(ret)){
// support method generator
co(ret).then(function(ret){
done(null, ret);
}).catch(done);
}
else{
done(null, ret);
}
}
}
catch(error){
done(error);
}
promise._context = self;
return promise;
}
classPrototype[name] = wrapFunction;
// add promise chain
PromisePrototype[name] = function(){
let self = this;
let args = arguments;
let context = self._context;
let promise = self.then(function(){
return wrapFunction.apply(context, args).then(function(ret){
return ret;
});
});
promise._context = context;
return promise;
};
};
newPromiseClass.addPropertie = function(name, value){
classPrototype[name] = value;
};
let defValue;
for(let name in classDefines){
defValue = classDefines[name];
if(typeof defValue === 'function'){
newPromiseClass.addMethod(name, defValue);
}
else{
newPromiseClass.addPropertie(name, defValue);
}
}
return newPromiseClass;
}
};
// check callback
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)){
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;