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

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;