-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpromise.js
137 lines (133 loc) · 4.66 KB
/
promise.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
// ## promise
// Returns a promise when passed a function with the signature ( resolve<Function>, reject<Function> ).
// ### Usage:
//
// function foo () {
// return W.promise( function ( resolve, reject ) {
// // if something went wrong
// reject( new Error( 'failed' ) );
// // if all went well
// resolve( 1, 2 );
// });
// }
//
// foo()
// .success( handler )
// .error( handler )
// .done( function ( err, a, b ) {} )
// .timeoutAfter( 2000 );
//
// ### Methods
// #### .success
// Called when resolved with any arguments passed to it.
// #### .error
// Called upon rejected. If no arguments are provided, the handler will be called with a single argument
// with is a n Error 'promise rejected'
// #### .done ( fn<Function> )
// Similar to a node callback, the signature of `fn` is `( errOrNull, arg1, arg2... )`.
// This handler will be called after `error` or `done` handlers. Like `error` if no arguments
// are provided to a `reject` the error will be an Error 'promise rejected'
// #### .timeout( delay<Number>, fn<Function>_optional_ )
// If `.timeoutAfter` is set, it will fire a reject with the error `promised timed out`. An optional `fn` may
// be passed with will also be be triggered
// ### Implmentation
function promise ( fn ) {
if ( typeof fn !== 'function' ) {
throw new Error( 'Promise constructor needs to be passed a function' );
}
var success = noop;
var error = noop;
var done = noop;
var debug = false;
var state = promise.PENDING;
var timeoutId;
var resolve;
var reject;
var thenables = [];
resolve = function () {
if ( state !== promise.PENDING ) { return; }
if ( thenables.length > 0 ) {
var thennable = thenables.shift();
if ( typeof thennable.onResolve === 'function' ) {
thennable.onResolve.apply( this, arguments )
.success( function () { resolve.apply( this, arguments ); } )
.error( function () { reject.apply( this, arguments ); } );
return;
} else {
resolve.apply( this, arguments );
return;
}
}
state = promise.FULFILLED;
clearTimeout( timeoutId );
success.apply( this, arguments );
Array.prototype.unshift.call( arguments, null );
done.apply( this, arguments );
if ( debug ) {
console.log( 'Promise resolved with', arguments );
}
};
reject = function () {
if ( state !== promise.PENDING ) { return; }
state = promise.REJECTED;
clearTimeout( timeoutId );
if ( thenables.length > 0 ) {
var thennable = thenables.shift();
if ( typeof thennable.onReject === 'function' ) {
thennable.onReject.apply( this, arguments );
return;
}
}
if ( arguments.length === 0 ) {
Array.prototype.unshift.call( arguments, new Error( 'Promise rejected' ) );
}
error.apply( this, arguments );
done.apply( this, arguments );
if ( debug ) {
console.log( 'Promise rejected with', arguments );
}
};
// Fire function
if ( typeof setImmediate === 'function' ) {
setImmediate( partial( fn, resolve, reject ) );
}
else if ( typeof process === 'object' && typeof process.nextTick === 'function' ) {
process.nextTick( partial( fn, resolve, reject ) );
}
else {
setTimeout( partial( fn, resolve, reject ), 0 );
}
var chain = {
success : function ( fn ) { success = fn; return chain; },
error : function ( fn ) { error = fn; return chain; },
done : function ( fn ) { done = fn; return chain; },
then : function ( onResolve, onReject ) {
if ( typeof onResolve !== 'function' && typeof onReject !== 'function' ) {
return chain;
}
thenables.push( { onResolve: onResolve, onReject: onReject } );
return chain;
},
timeoutAfter : function ( delay, fn ) {
timeoutId = setTimeout( function () {
success = noop;
reject( new Error( 'Promise timed out' ) );
reject = noop;
if ( typeof fn === 'function' ) {
fn();
}
}, delay );
return chain;
},
debug : function () {
debug = true;
},
getState : function () {
return state;
}
};
return chain;
}
promise.FULFILLED = 1;
promise.PENDING = 0;
promise.REJECTED = -1;