Merge pull request #853 from isiahmeadows/prop-fix

Isolate m.prop() and m.deferred() implementations (mostly)
This commit is contained in:
Isiah Meadows 2015-12-15 06:41:09 -05:00
commit e63a6f37bd
5 changed files with 176 additions and 131 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
node_modules
archive

View file

@ -1201,9 +1201,52 @@ void (function (global, factory) { // eslint-disable-line
return prop
}
function isPromise(object) {
return object != null && (isObject(object) || isFunction(object)) &&
isFunction(object.then)
}
function simpleResolve(p, callback) {
if (p.then) {
return p.then(callback)
} else {
return callback()
}
}
function propify(promise) {
var prop = m.prop()
promise.then(prop)
prop.then = function (resolve, reject) {
return promise.then(function () {
return resolve(prop())
}, reject)
}
prop.catch = function (reject) {
return promise.then(function () {
return prop()
}, reject)
}
prop.finally = function (callback) {
return promise.then(function (value) {
return simpleResolve(callback(), function () {
return value
})
}, function (reason) {
return simpleResolve(callback(), function () {
throw reason
})
})
}
return prop
}
m.prop = function (store) {
if ((store != null && isObject(store) || isFunction(store)) &&
isFunction(store.then)) {
if (isPromise(store)) {
return propify(store)
} else {
return gettersetter(store)
@ -1741,164 +1784,167 @@ void (function (global, factory) { // eslint-disable-line
function Deferred(onSuccess, onFailure) {
var self = this
var state = 0
var promiseValue = 0
var promiseValue
var next = []
var func = push
self.promise = {}
function coerce(value, next, error) {
if (isPromise(value)) {
return value.then(function (value) {
coerce(value, next, error)
}, function (e) {
coerce(e, error, error)
})
} else {
return next(promiseValue = value)
}
}
function resolve(deferred) {
deferred.resolve(promiseValue)
}
function reject(deferred) {
deferred.reject(promiseValue)
}
function push(deferred) {
next.push(deferred)
}
function init(promise) {
if (func !== reject) promise(promiseValue)
return promise
}
self.resolve = function (value) {
if (!state) {
promiseValue = value
state = RESOLVING
fire()
if (func === push) {
fire(RESOLVING, value, self)
}
return this
}
self.reject = function (value) {
if (!state) {
promiseValue = value
state = REJECTING
fire()
if (func === push) {
fire(REJECTING, value, self)
}
return this
}
self.promise.then = function (onSuccess, onFailure) {
var deferred = new Deferred(onSuccess, onFailure)
if (state === RESOLVED) {
deferred.resolve(promiseValue)
} else if (state === REJECTED) {
deferred.reject(promiseValue)
} else {
next.push(deferred)
}
return deferred.promise
self.promise = function (value) {
if (arguments.length) coerce(value, noop, noop)
return func !== reject ? promiseValue : undefined
}
function finish(type) {
state = type || REJECTED
forEach(next, function (deferred) {
if (state === RESOLVED) {
deferred.resolve(promiseValue)
} else {
deferred.reject(promiseValue)
}
self.promise.then = function (onSuccess, onFailure) {
var deferred = new Deferred(onSuccess, onFailure)
func(deferred)
return init(deferred.promise)
}
self.promise.catch = function (callback) {
return self.promise.then(null, callback)
}
self.promise.finally = function (callback) {
function _callback() {
var p = new Deferred().resolve(callback()).promise
if (func !== reject) p(promiseValue)
return p
}
return self.promise.then(function () {
return _callback().then(function () {
return promiseValue
})
}, function () {
_callback().then(function () {
throw promiseValue
})
})
}
function thennable(then, success, fail, notThennable) {
if (((promiseValue != null && isObject(promiseValue)) ||
isFunction(promiseValue)) && isFunction(then)) {
try {
// count protects against abuse calls from spec checker
var count = 0
then.call(promiseValue, function (value) {
if (count++) return
promiseValue = value
success()
}, function (value) {
if (count++) return
promiseValue = value
fail()
})
} catch (e) {
m.deferred.onerror(e)
promiseValue = e
fail()
}
} else {
notThennable()
function run(callback) {
func = callback
forEach(next, callback)
// Clear these (which hold all the extra references)
finish = fire = null // eslint-disable-line no-func-assign
}
function finish(value, state) {
coerce(value, function () {
run(state === RESOLVED ? resolve : reject)
}, function () {
run(reject)
})
}
function doThen(value, deferred) {
// count protects against abuse calls from spec checker
var count = 0
try {
return value.then(function (value) {
if (count++) return
fire(RESOLVING, value, deferred)
}, function (value) {
if (count++) return
fire(REJECTING, value, deferred)
})
} catch (e) {
m.deferred.onerror(e)
return fire(REJECTING, e, deferred)
}
}
function fire() {
// check if it's a thenable
var then
function notThennable(value, state, deferred) {
try {
then = promiseValue && promiseValue.then
if (state === RESOLVING && isFunction(onSuccess)) {
value = onSuccess(value)
} else if (state === REJECTING && isFunction(onFailure)) {
value = onFailure(value)
state = RESOLVING
}
} catch (e) {
m.deferred.onerror(e)
promiseValue = e
state = REJECTING
return fire()
return finish(e, REJECTED)
}
if (value === deferred) {
return finish(TypeError(), REJECTED)
} else {
return finish(value, state === RESOLVING ? RESOLVED : REJECTED)
}
}
function fire(state, value, deferred) {
// check if it's a thenable
var thenable
try {
thenable = isPromise(value)
} catch (e) {
m.deferred.onerror(e)
return fire(REJECTING, e, deferred)
}
if (state === REJECTING) {
m.deferred.onerror(promiseValue)
m.deferred.onerror(value)
}
thennable(then, function () {
state = RESOLVING
fire()
}, function () {
state = REJECTING
fire()
}, function () {
try {
if (state === RESOLVING && isFunction(onSuccess)) {
promiseValue = onSuccess(promiseValue)
} else if (state === REJECTING && isFunction(onFailure)) {
promiseValue = onFailure(promiseValue)
state = RESOLVING
}
} catch (e) {
m.deferred.onerror(e)
promiseValue = e
return finish()
}
if (promiseValue === self) {
promiseValue = TypeError()
finish()
} else {
thennable(then, function () {
finish(RESOLVED)
}, finish, function () {
finish(state === RESOLVING && RESOLVED)
})
}
})
if (thenable) {
return doThen(value, deferred)
} else {
return notThennable(value, state, deferred)
}
}
}
m.deferred = function () {
var deferred = new Deferred()
deferred.promise = propify(deferred.promise)
return deferred
return new Deferred()
}
function propify(promise, initialValue) {
var prop = m.prop(initialValue)
promise.then(prop)
prop.then = function (resolve, reject) {
return propify(promise.then(resolve, reject), initialValue)
}
prop.catch = prop.then.bind(null, null)
prop.finally = function (callback) {
function _callback() {
return m.deferred().resolve(callback()).promise
}
return prop.then(function (value) {
return propify(_callback().then(function () {
return value
}), initialValue)
}, function (reason) {
return propify(_callback().then(function () {
throw new Error(reason)
}), initialValue)
})
}
return prop
}
m.deferred.prototype = Deferred.prototype
m.deferred.prototype.constructor = m.deferred
function isNativeError(e) {
return e instanceof EvalError ||
@ -1917,7 +1963,7 @@ void (function (global, factory) { // eslint-disable-line
}
m.sync = function (args) {
var deferred = m.deferred()
var deferred = new Deferred()
var outstanding = args.length
var results = new Array(outstanding)
var method = "resolve"
@ -2086,7 +2132,6 @@ void (function (global, factory) { // eslint-disable-line
m.request = function (options) {
if (options.background !== true) m.startComputation()
var deferred = new Deferred()
var serialize = identity
@ -2146,7 +2191,7 @@ void (function (global, factory) { // eslint-disable-line
}
ajax(options)
deferred.promise = propify(deferred.promise, options.initialValue)
deferred.promise(options.initialValue)
return deferred.promise
}

2
mithril.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -117,7 +117,6 @@ describe("m.deferred()", function () {
expect(value2()).to.be.an("error")
})
// FIXME: this is a bug.
it("synchronously throws subclasses of Errors on creation", function () {
expect(function () {
m.deferred().reject(new TypeError())