-
Notifications
You must be signed in to change notification settings - Fork 9
/
Callback.js
175 lines (159 loc) · 4.49 KB
/
Callback.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
'use strict';
/**
* 创建一个Callback对象.
* @class
* @example
* 使用Callback实现的计时器:
* function wait (t) {
* return new Callback(function (callback) {
* setTimeout(callback,t*1000);
* });
* }
* @param {Callback~executor} fn - 用于初始化Callback对象的执行函数.
*/
/**
* 执行函数.
* @callback Callback~executor
* @param {function} 操作完成声明函数,该函数被调用后,
* 由此执行函数创建的Callback对象的状态将变为`Done`,
* 其回调函数队列将被调用,参数将传入第一个回调函数.
*/
function Callback (fn) {
/**
* 回调函数队列.
* @private {function[]}
*/
this._callbacks = [];
/**
* 回调函数的上下文对象队列.
* @private {Object[]}
*/
this._thisArray = [];
/**
* Callback的状态.
* `_done` 为false时表示该Callback处于pending状态.
* `_done` 为true时表示该Callback处于done状态.
* @private {boolean}
*/
this._done = false;
/**
* 调用首个回调函数时的参数.
* @private
*/
this._arg = [];
fn(this._handleCallbacks.bind(this));
}
/**
* 创建一个永远处于pending状态的Callback.
* @returns {Callback} 永远处于pending状态的Callback.
*/
Callback.never = function () {
return new Callback(function () {});
};
/**
* 创建一个立即回调的Callback.
* 通常用于同步操作和异步操作混合的场合,提供语法糖功能.
* @returns {Callback} 立即回调的Callback,调用参数会传递到回调函数.
*/
Callback.immediately = function () {
var arg = arguments;
return new Callback(function (callback) {
callback.apply(this,arg);
});
};
/**
* 异步的循环`forEach`,语法类似于`Array.prototype.forEach`.
* @example
* `arr`为一个数据数组,`doSomethingAsynWith`是一个返回Callback对象的异步处理函数.
* 现在要遍历`arr`,对每一个元素执行异步处理:
* Callback.forEach(arr,function (item,index,arr) {
* return doSomethingAsynWith(item);
* },this).callback(this,function () {
* // all done;
* });
* @returns {Callback}.
*/
Callback.forEach = function (arr,fn,thisp) {
return arr.reduce(function (chain,item,i,array) {
return chain.callback(thisp,fn.bind(thisp,item,i,array));
},Callback.immediately());
};
/**
* 异步的循环`for`.
* @example
* `doSomethingAsyn`是一个返回Callback对象的异步处理函数.
* 现在循环执行10次`doSomethingAsyn`:
* Callback.for(this,1,10,function (i) {
* return doSomethingAsyn(i);
* },this).callback(this,function () {
* // all done;
* });
* @returns {Callback}.
*/
Callback.for = function (thisp,min,max,fn) {
var chain = Callback.immediately();
for (var i = min; i <= max; i++) {
chain.callback(thisp,fn.bind(thisp,i));
}
return chain;
};
/**
* 异步的循环`loop`.
* @example
* `doSomethingAsyn`是一个返回Callback对象的异步处理函数.
* 现在循环执行10次`doSomethingAsyn`:
* Callback.loop(this,10,function () {
* return doSomethingAsyn();
* },this).callback(this,function () {
* // all done;
* });
* @returns {Callback}.
*/
Callback.loop = function (thisp,n,fn) {
var chain = Callback.immediately();
while (n--) {
chain.callback(thisp,fn.bind(thisp));
}
return chain;
}
/**
* 处理回调函数队列. 该方法总是在Callback对象的状态变为done时调用.
* @private
*/
Callback.prototype._handleCallbacks = function () {
this._done = true;
if (this._callbacks.length) {
var callback = this._callbacks.shift();
var thisp = this._thisArray.shift();
var returnValue = callback.apply(thisp,arguments);
if (isObj(returnValue) && isFunc(returnValue.callback)) {
this._done = false;
returnValue.callback(this,this._handleCallbacks);
} else {
this._handleCallbacks(returnValue);
}
} else {
this._arg = arguments;
}
};
/**
* 向Callback对象添加回调函数. 可以链式调用.
* @public
* @param thisp - 绑定的上下文对象.
* @param {function} func - 回调函数.
* 回调函数的返回值为Callback对象时,其后续的回调函数会在Callback的状态变为done时才执行,
* 否则该回调函数执行后,后续的回调函数立即执行,并把返回值作为参数传入.
*/
Callback.prototype.callback = function (thisp,func) {
if (arguments.length !== 2) {
debugger;
console.warn('thisp is not specified!');
func = thisp;
thisp = this;
}
this._thisArray.push(thisp);
this._callbacks.push(func);
if (this._done) this._handleCallbacks.apply(this,this._arg);
return this;
};
global.Callback = Callback;