316 lines
4.9 KiB
JavaScript
316 lines
4.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @constructor
|
|
*/
|
|
function Promise(resolver) {
|
|
if (typeof resolver !== 'function' && typeof resolver !== 'undefined') {
|
|
throw new TypeError();
|
|
}
|
|
|
|
if (typeof this !== 'object' || (this && this.then)) {
|
|
throw new TypeError();
|
|
}
|
|
|
|
var self = this;
|
|
|
|
// states
|
|
// 0: pending
|
|
// 1: resolving
|
|
// 2: rejecting
|
|
// 3: resolved
|
|
// 4: rejected
|
|
var state = 0;
|
|
var val = 0;
|
|
var next = [];
|
|
var fn = null;
|
|
var er = null;
|
|
|
|
this.promise = this;
|
|
|
|
this.resolve = function resolve(v) {
|
|
fn = self.fn;
|
|
er = self.er;
|
|
|
|
if (!state) {
|
|
val = v;
|
|
state = 1;
|
|
|
|
setImmediate(fire);
|
|
}
|
|
|
|
return self;
|
|
};
|
|
|
|
this.reject = function reject(v) {
|
|
fn = self.fn;
|
|
er = self.er;
|
|
|
|
if (!state) {
|
|
val = v;
|
|
state = 2;
|
|
|
|
setImmediate(fire);
|
|
}
|
|
|
|
return self;
|
|
};
|
|
|
|
this._p = 1;
|
|
|
|
this.then = function then(_fn, _er) {
|
|
if (!(this._p === 1)) {
|
|
throw new TypeError();
|
|
}
|
|
|
|
var p = new Promise();
|
|
|
|
p.fn = _fn;
|
|
p.er = _er;
|
|
|
|
switch (state) {
|
|
case 3:
|
|
p.resolve(val);
|
|
break;
|
|
case 4:
|
|
p.reject(val);
|
|
break;
|
|
default:
|
|
next.push(p);
|
|
break;
|
|
}
|
|
|
|
return p;
|
|
};
|
|
|
|
this.catch = function _catch(_er) {
|
|
return self.then(null, _er);
|
|
};
|
|
|
|
var finish = function finish(type) {
|
|
state = type || 4;
|
|
|
|
next.map(function(p) {
|
|
state === 3 && p.resolve(val) || p.reject(val);
|
|
});
|
|
};
|
|
|
|
try {
|
|
if (typeof resolver === 'function') {
|
|
resolver(this.resolve, this.reject);
|
|
}
|
|
} catch (e) {
|
|
this.reject(e);
|
|
}
|
|
|
|
return this;
|
|
|
|
// ref: reference to 'then' function
|
|
// cb, ec, cn: successCallback, failureCallback, notThennableCallback
|
|
function thennable (ref, cb, ec, cn) {
|
|
if ((typeof val === 'object' || typeof val === 'function') && typeof ref === 'function') {
|
|
try {
|
|
// cnt protects against abuse calls from spec checker
|
|
var cnt = 0;
|
|
ref.call(val, function(v) {
|
|
if (cnt++) {
|
|
return;
|
|
}
|
|
|
|
val = v;
|
|
|
|
cb();
|
|
}, function(v) {
|
|
if (cnt++) {
|
|
return;
|
|
}
|
|
|
|
val = v;
|
|
|
|
ec();
|
|
})
|
|
} catch (e) {
|
|
val = e;
|
|
|
|
ec();
|
|
}
|
|
} else {
|
|
cn();
|
|
}
|
|
}
|
|
|
|
function fire() {
|
|
// check if it's a thenable
|
|
var ref;
|
|
|
|
try {
|
|
ref = val && val.then;
|
|
} catch (e) {
|
|
val = e;
|
|
state = 2;
|
|
|
|
return fire();
|
|
}
|
|
|
|
thennable(ref, function() {
|
|
state = 1;
|
|
|
|
fire();
|
|
}, function() {
|
|
state = 2;
|
|
|
|
fire();
|
|
}, function() {
|
|
try {
|
|
if (state === 1 && typeof fn === 'function') {
|
|
val = fn(val);
|
|
} else if (state === 2 && typeof er === 'function') {
|
|
val = er(val);
|
|
|
|
state = 1;
|
|
}
|
|
} catch (e) {
|
|
val = e;
|
|
|
|
return finish();
|
|
}
|
|
|
|
if (val === self) {
|
|
val = TypeError();
|
|
|
|
finish();
|
|
} else {
|
|
thennable(ref, function() {
|
|
finish(3);
|
|
}, finish, function() {
|
|
finish(state === 1 && 3);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Promise.resolve = function resolve(value) {
|
|
if (!(this._p === 1)) {
|
|
throw new TypeError();
|
|
}
|
|
|
|
if (value instanceof Promise) {
|
|
return value;
|
|
}
|
|
|
|
return new Promise(function(resolve) {
|
|
resolve(value);
|
|
});
|
|
};
|
|
|
|
Promise.reject = function reject(value) {
|
|
if (!(this._p === 1)) {
|
|
throw new TypeError();
|
|
}
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
reject(value);
|
|
});
|
|
};
|
|
|
|
Promise.all = function all(arr) {
|
|
if (!(this._p === 1)) {
|
|
throw new TypeError();
|
|
}
|
|
|
|
if (!(arr instanceof Array)) {
|
|
return Promise.reject(TypeError());
|
|
}
|
|
|
|
var p = new Promise();
|
|
|
|
function done(e, v) {
|
|
if (v) {
|
|
return p.resolve(v);
|
|
}
|
|
|
|
if (e) {
|
|
return p.reject(e);
|
|
}
|
|
|
|
var unresolved = arr.reduce(function(cnt, v) {
|
|
if (v && v.then) {
|
|
return cnt + 1;
|
|
}
|
|
|
|
return cnt;
|
|
}, 0);
|
|
|
|
if (unresolved === 0) {
|
|
p.resolve(arr);
|
|
}
|
|
|
|
arr.map(function(v, i) {
|
|
if (v && v.then) {
|
|
v.then(function(r) {
|
|
arr[i] = r;
|
|
done();
|
|
return r;
|
|
}, done);
|
|
}
|
|
});
|
|
}
|
|
|
|
done();
|
|
|
|
return d;
|
|
}
|
|
|
|
Promise.race = function race(arr) {
|
|
if (!(this._p === 1)) {
|
|
throw TypeError();
|
|
}
|
|
|
|
if (!(arr instanceof Array)) {
|
|
return Promise.reject(TypeError());
|
|
}
|
|
|
|
if (arr.length === 0) {
|
|
return new Promise();
|
|
}
|
|
|
|
var p = new Promise();
|
|
|
|
function done(e, v) {
|
|
if (v) {
|
|
return p.resolve(v);
|
|
}
|
|
|
|
if (e) {
|
|
return p.reject(e);
|
|
}
|
|
|
|
var unresolved = arr.reduce(function(cnt, v) {
|
|
if (v && v.then) {
|
|
return cnt + 1;
|
|
}
|
|
|
|
return cnt;
|
|
}, 0);
|
|
|
|
if (unresolved === 0) {
|
|
p.resolve(arr);
|
|
}
|
|
|
|
arr.map(function(v, i) {
|
|
if (v && v.then) {
|
|
v.then(function(r) {
|
|
done(null, r);
|
|
}, done);
|
|
}
|
|
});
|
|
}
|
|
|
|
done();
|
|
|
|
return p;
|
|
}
|
|
|
|
Promise._p = 1;
|