nearly Promises/A+ compliant via Promiz.mithril.js
This commit is contained in:
parent
0f5d051d4b
commit
54474f5aef
2 changed files with 159 additions and 51 deletions
176
mithril.js
176
mithril.js
|
|
@ -509,48 +509,156 @@ Mithril = m = new function app(window) {
|
|||
}
|
||||
|
||||
var none = {}
|
||||
m.deferred = function() {
|
||||
var resolvers = [], rejecters = [], resolved = none, rejected = none, promise = m.prop()
|
||||
var object = {
|
||||
resolve: function(value) {
|
||||
if (resolved === none) promise(resolved = value)
|
||||
for (var i = 0; i < resolvers.length; i++) resolvers[i](value)
|
||||
resolvers.length = rejecters.length = 0
|
||||
},
|
||||
reject: function(value) {
|
||||
if (rejected === none) rejected = value
|
||||
for (var i = 0; i < rejecters.length; i++) rejecters[i](value)
|
||||
resolvers.length = rejecters.length = 0
|
||||
},
|
||||
promise: promise
|
||||
m.deferred = function () {
|
||||
|
||||
// Promiz.mithril.js | Zolmeister | MIT
|
||||
function Deferred(fn, er) {
|
||||
// states
|
||||
// 0: pending
|
||||
// 1: resolving
|
||||
// 2: rejecting
|
||||
// 3: resolved
|
||||
// 4: rejected
|
||||
var self = this,
|
||||
state = 0,
|
||||
val = 0,
|
||||
next = [];
|
||||
|
||||
self['promise'] = self
|
||||
|
||||
self['resolve'] = function (v) {
|
||||
if (!state) {
|
||||
val = v
|
||||
state = 1
|
||||
|
||||
fire()
|
||||
}
|
||||
object.promise.resolvers = resolvers
|
||||
object.promise.then = function(success, error) {
|
||||
var next = m.deferred()
|
||||
if (!success) success = identity
|
||||
if (!error) error = identity
|
||||
function callback(method, callback) {
|
||||
return function(value) {
|
||||
try {
|
||||
var result = callback(value)
|
||||
if (result && typeof result.then == "function") result.then(next[method], error)
|
||||
else next[method](result !== undefined ? result : value)
|
||||
return this
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof Error && e.constructor !== Error) throw e
|
||||
else next.reject(e)
|
||||
|
||||
self['reject'] = function (v) {
|
||||
if (!state) {
|
||||
val = v
|
||||
state = 2
|
||||
|
||||
fire()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
self['then'] = function (fn, er) {
|
||||
var d = new Deferred(fn, er)
|
||||
if (state == 3) {
|
||||
d.resolve(val)
|
||||
}
|
||||
else if (state == 4) {
|
||||
d.reject(val)
|
||||
}
|
||||
if (resolved !== none) callback("resolve", success)(resolved)
|
||||
else if (rejected !== none) callback("reject", error)(rejected)
|
||||
else {
|
||||
resolvers.push(callback("resolve", success))
|
||||
rejecters.push(callback("reject", error))
|
||||
next.push(d)
|
||||
}
|
||||
return next.promise
|
||||
return d
|
||||
}
|
||||
return object
|
||||
|
||||
var finish = function (type) {
|
||||
state = type || 4
|
||||
next.map(function (p) {
|
||||
state == 3 && p.resolve(val) || p.reject(val)
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function newPromisedProp(prop, promise) {
|
||||
prop.then = function () {
|
||||
var newProp = m.prop(prop())
|
||||
return newPromisedProp(newProp,
|
||||
promise.then.apply(promise, arguments).then(newProp))
|
||||
}
|
||||
prop.promise = prop
|
||||
prop.resolve = function (val) {
|
||||
prop(val)
|
||||
promise = promise.resolve.apply(promise, arguments)
|
||||
return prop
|
||||
}
|
||||
prop.reject = function () {
|
||||
promise = promise.reject.apply(promise, arguments)
|
||||
return prop
|
||||
}
|
||||
|
||||
return prop
|
||||
}
|
||||
|
||||
return newPromisedProp(m.prop(), new Deferred())
|
||||
}
|
||||
m.sync = function(args) {
|
||||
var method = "resolve"
|
||||
|
|
|
|||
|
|
@ -1322,7 +1322,7 @@ function testMithril(mock) {
|
|||
var error = m.prop("no error")
|
||||
var prop = m.request({method: "GET", url: "test", deserialize: function() {throw new Error("error occurred")}}).then(null, error)
|
||||
mock.XMLHttpRequest.$instances.pop().onreadystatechange()
|
||||
return prop() === undefined && error().message === "error occurred"
|
||||
return prop().message === "error occurred" && error().message === "error occurred"
|
||||
})
|
||||
test(function() {
|
||||
var error = m.prop("no error"), exception
|
||||
|
|
@ -1365,7 +1365,7 @@ function testMithril(mock) {
|
|||
test(function() {
|
||||
var value
|
||||
var deferred = m.deferred()
|
||||
deferred.promise.then(null, function(data) {return "foo"}).then(null, function(data) {value = data})
|
||||
deferred.promise.then(null, function(data) {return "foo"}).then(function(data) {value = data})
|
||||
deferred.reject("test")
|
||||
return value === "foo"
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue