You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
5.6 KiB
178 lines
5.6 KiB
var Class = require('./Class');
|
|
var isObj = require('./isObj');
|
|
var isFn = require('./isFn');
|
|
var State = require('./State');
|
|
var bind = require('./bind');
|
|
var nextTick = require('./nextTick');
|
|
var noop = require('./noop');
|
|
var toArr = require('./toArr');
|
|
var Promise = (exports = Class(
|
|
{
|
|
initialize: function Promise(fn) {
|
|
if (!isObj(this))
|
|
throw new TypeError('Promises must be constructed via new');
|
|
if (!isFn(fn)) throw new TypeError(fn + ' is not a function');
|
|
var self = this;
|
|
this._state = new State('pending', {
|
|
fulfill: {
|
|
from: 'pending',
|
|
to: 'fulfilled'
|
|
},
|
|
reject: {
|
|
from: 'pending',
|
|
to: 'rejected'
|
|
},
|
|
adopt: {
|
|
from: 'pending',
|
|
to: 'adopted'
|
|
}
|
|
})
|
|
.on('fulfill', assignVal)
|
|
.on('reject', assignVal)
|
|
.on('adopt', assignVal);
|
|
function assignVal(val) {
|
|
self._value = val;
|
|
}
|
|
this._handled = false;
|
|
this._value = undefined;
|
|
this._deferreds = [];
|
|
doResolve(fn, this);
|
|
},
|
|
catch: function(onRejected) {
|
|
return this.then(null, onRejected);
|
|
},
|
|
then: function(onFulfilled, onRejected) {
|
|
var promise = new Promise(noop);
|
|
handle(this, new Handler(onFulfilled, onRejected, promise));
|
|
return promise;
|
|
}
|
|
},
|
|
{
|
|
all: function(arr) {
|
|
var args = toArr(arr);
|
|
return new Promise(function(resolve, reject) {
|
|
if (args.length === 0) return resolve([]);
|
|
var remaining = args.length;
|
|
function res(i, val) {
|
|
try {
|
|
if (val && (isObj(val) || isFn(val))) {
|
|
var then = val.then;
|
|
if (isFn(then)) {
|
|
then.call(
|
|
val,
|
|
function(val) {
|
|
res(i, val);
|
|
},
|
|
reject
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
args[i] = val;
|
|
if (--remaining === 0) resolve(args);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}
|
|
for (var i = 0; i < args.length; i++) res(i, args[i]);
|
|
});
|
|
},
|
|
resolve: function(val) {
|
|
if (val && isObj(val) && val.constructor === Promise) return val;
|
|
return new Promise(function(resolve) {
|
|
resolve(val);
|
|
});
|
|
},
|
|
reject: function(val) {
|
|
return new Promise(function(resolve, reject) {
|
|
reject(val);
|
|
});
|
|
},
|
|
race: function(values) {
|
|
return new Promise(function(resolve, reject) {
|
|
for (var i = 0, len = values.length; i < len; i++) {
|
|
values[i].then(resolve, reject);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
));
|
|
var Handler = Class({
|
|
initialize: function Handler(onFulfilled, onRejected, promise) {
|
|
this.onFulfilled = isFn(onFulfilled) ? onFulfilled : null;
|
|
this.onRejected = isFn(onRejected) ? onRejected : null;
|
|
this.promise = promise;
|
|
}
|
|
});
|
|
function reject(self, err) {
|
|
self._state.reject(err);
|
|
finale(self);
|
|
}
|
|
function resolve(self, val) {
|
|
try {
|
|
if (val === self)
|
|
throw new TypeError('A promise cannot be resolved with itself');
|
|
if (val && (isObj(val) || isFn(val))) {
|
|
var then = val.then;
|
|
if (val instanceof Promise) {
|
|
self._state.adopt(val);
|
|
return finale(self);
|
|
}
|
|
if (isFn(then)) return doResolve(bind(then, val), self);
|
|
}
|
|
self._state.fulfill(val);
|
|
finale(self);
|
|
} catch (e) {
|
|
reject(self, e);
|
|
}
|
|
}
|
|
function finale(self) {
|
|
for (var i = 0, len = self._deferreds.length; i < len; i++) {
|
|
handle(self, self._deferreds[i]);
|
|
}
|
|
self._deferreds = null;
|
|
}
|
|
function handle(self, deferred) {
|
|
while (self._state.is('adopted')) self = self._value;
|
|
if (self._state.is('pending')) return self._deferreds.push(deferred);
|
|
self._handled = true;
|
|
nextTick(function() {
|
|
var isFulfilled = self._state.is('fulfilled');
|
|
var cb = isFulfilled ? deferred.onFulfilled : deferred.onRejected;
|
|
if (cb === null)
|
|
return (isFulfilled ? resolve : reject)(
|
|
deferred.promise,
|
|
self._value
|
|
);
|
|
var ret;
|
|
try {
|
|
ret = cb(self._value);
|
|
} catch (e) {
|
|
return reject(deferred.promise, e);
|
|
}
|
|
resolve(deferred.promise, ret);
|
|
});
|
|
}
|
|
function doResolve(fn, self) {
|
|
var done = false;
|
|
try {
|
|
fn(
|
|
function(val) {
|
|
if (done) return;
|
|
done = true;
|
|
resolve(self, val);
|
|
},
|
|
function(reason) {
|
|
if (done) return;
|
|
done = true;
|
|
reject(self, reason);
|
|
}
|
|
);
|
|
} catch (e) {
|
|
if (done) return;
|
|
done = true;
|
|
reject(self, e);
|
|
}
|
|
}
|
|
|
|
module.exports = exports;
|
|
|