-
Notifications
You must be signed in to change notification settings - Fork 1
/
promise.js
128 lines (116 loc) · 3.31 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
var State = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
};
var transition = function(state, value) {
if (this.state == state || // must change the state
this.state !== State.PENDING || // can only change from pending
(state != State.FULFILLED && // can only change to fulfill or reject
state != State.REJECTED) ||
arguments.length < 2) { // must provide value/reason
return false;
}
this.state = state; // change state
this.value = value;
this.run(); // we’ll see this later
};
var then = function(onFulfilled, onRejected) {
// need to return a promise
var promise = new Promise();
this.queue.push({
fulfill : typeof onFulfilled == 'function' && onFulfilled,
reject : typeof onRejected == 'function' && onRejected,
promise: promise
});
this.run(); // We’ll see this later
return promise;
};
var resolve = function(promise, x) {
if (promise === x) {
promise.transition(State.REJECTED, new TypeError());
} else if (x && x.constructor == Promise) { // must know it’s implementation
if (x.state == State.PENDING) { // 2.3.2.1
x.then(function(value) {
resolve(promise, value);
}, function(reason) {
promise.transition(State.REJECTED, reason);
});
} else {
promise.transition(x.state, x.value);
}
} else if ((typeof x == 'object' || typeof x == 'function') && x != null) {
var called = false;
try {
var then = x.then;
if (typeof then == 'function') {
then.call(x, function(y) {
called || resolve(promise, y);
called = true;
}, function(r) {
called || promise.transition(State.REJECTED, r);
called = true;
});
} else {
promise.transition(State.FULFILLED, x);
}
} catch(e) {
called || promise.transition(State.REJECTED, e);
}
} else {
promise.transition(State.FULFILLED, x);
}
};
var run = function() {
if (this.state == State.PENDING) return;
var self = this;
setTimeout(function() {
while(self.queue.length) {
var obj = self.queue.shift();
try {
// resolve returned promise based on return
var value = (self.state == State.FULFILLED ?
(obj.fulfill || function(x) {return x;}) :
(obj.reject || function(x) {throw x;}))
(self.value);
} catch (e) {
// reject if an error is thrown
obj.promise.transition(State.REJECTED, e);
continue;
}
resolve(obj.promise, value);
}
}, 0);
};
var Promise = function(fn) {
var self = this;
this.state = State.PENDING;
this.queue = [];
fn && fn(function(value) {
resolve(self, value);
}, function(reason) {
self.transition(State.REJECTED, reason);
});
};
Promise.prototype.transition = transition;
Promise.prototype.run = run;
Promise.prototype.then = then;
module.exports = {
resolved: function(value) {
return new Promise(function(res) {res(value);});
},
rejected: function(reason) {
return new Promise(function(res, rej) {rej(reason);});
},
deferred: function() {
var resolve, reject;
return {
promise: new Promise(function(res, rej) {
resolve = res;
reject = rej;
}),
resolve: resolve,
reject: reject
};
}
};