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;
|
|
|