Created
May 21, 2012 17:23
-
-
Save Gozala/2763402 to your computer and use it in GitHub Desktop.
Experimenting with promises.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* vim:set ts=2 sw=2 sts=2 expandtab */ | |
/*jshint undef: true es5: true node: true browser: true devel: true | |
forin: true latedef: false */ | |
/*global define: true, Cu: true, __URI__: true */ | |
;(function(id, factory) { // Module boilerplate :( | |
if (typeof(define) === 'function') { // RequireJS | |
define(factory); | |
} else if (typeof(require) === 'function') { // CommonJS | |
factory.call(this, require, exports, module); | |
} else if (String(this).indexOf('BackstagePass') >= 0) { // JSM | |
factory(function require(uri) { | |
var imports = {}; | |
this['Components'].utils.import(uri, imports); | |
return imports; | |
}, this, { uri: __URI__, id: id }); | |
this.EXPORTED_SYMBOLS = Object.keys(this); | |
} else { // Browser or alike | |
var globals = this; | |
factory(function require(id) { | |
return globals[id]; | |
}, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id }); | |
} | |
}).call(this, 'promise', function(require, exports, module) { | |
'use strict'; | |
// Shim for: http://wiki.ecmascript.org/doku.php?id=harmony:private_name_objects | |
function Name(name) { return (name || Math.random().toString(32).substr(2)) + '@' + module.uri } | |
// IWatchable | |
var _watchers = Name('watchers'); | |
function watchers(value) { | |
/** | |
Returns all the watchers of the given value. | |
**/ | |
return value && value[_watchers]; | |
} | |
exports.watchers = watchers; | |
function watchable(value) { | |
/** | |
Initilalizes value as watchable. | |
**/ | |
if (!(_watchers in value)) | |
value[_watchers] = []; | |
return value | |
} | |
exports.watchable = watchable; | |
function watch(value, watcher) { | |
/** | |
Adds new watcher of the value. | |
**/ | |
var queue = watchers(value); | |
if (queue && queue.indexOf(watcher) < 0) | |
queue.push(watcher); | |
} | |
exports.watch = watch; | |
function unwatch(value, watcher) { | |
var queue = watchers(value); | |
var index = queue && queue.indexOf(watcher) | |
if (queue && index >= 0) | |
queue.splice(index, 1) | |
} | |
// ISignals | |
function signal(value, message) { | |
/** | |
Signals `message` to the watchers of the given | |
`value`. | |
**/ | |
watchers(value).slice().forEach(function(watcher) { | |
watcher(message); | |
}); | |
} | |
exports.signal = signal; | |
// IAtom | |
var _value = Name('value'); | |
var _atom = Name('atom'); | |
function atom(value) { | |
value = value || {}; | |
value[_value] = undefined; | |
value[_atom] = true; | |
return value; | |
} | |
function isAtom(value) { | |
return value && value[_atom]; | |
} | |
function get(value) { | |
return isAtom(value) ? value[_value] : value; | |
} | |
function set(source, value) { | |
if (isAtom(source)) source[_value] = value | |
} | |
// IPending | |
var pending = Name('realized') | |
function isPending(value) { | |
return value && value[pending] | |
} | |
exports.isPending = isPending | |
function suspend(value) { | |
if (value) value[pending] = true | |
return value | |
} | |
exports.suspend = suspend | |
function realize(value) { | |
if (value) value[pending] = false | |
return value | |
} | |
exports.realize = realize | |
// IEventual | |
function eventual(value) { | |
return watchable(suspend(atom(value))); | |
} | |
exports.eventual = eventual; | |
function when(value, observer) { | |
if (isPending(value)) | |
watch(value, observer) | |
else | |
observer(get(value)) | |
return value | |
} | |
function deliver(eventual, value) { | |
if (isPending(eventual)) { | |
set(eventual, value); | |
var observers = watchers(eventual); | |
while (observers.length) | |
when(value, observers.shift()); | |
realize(eventual); | |
} | |
return eventual | |
} | |
// Markers | |
function marker(name) { | |
return function mark(value) { | |
return { name: name, is: mark, value: value }; | |
}; | |
} | |
function is(value, marker) { | |
return value && value.is === marker; | |
} | |
var rejection = marker('rejection'); | |
// Promise | |
function Promise() { | |
return eventual(this) | |
} | |
Promise.prototype.then = function then(onResolve, onReject) { | |
var result = promise() | |
when(this, function(value) { | |
if (is(value, rejection)) | |
deliver(result, onReject(value.value)) | |
else | |
deliver(result, onResolve(value)) | |
}) | |
return result | |
} | |
function resolve(promise, value) { | |
deliver(promise, value) | |
} | |
function reject(promise, reason) { | |
deliver(promise, rejection(reason)) | |
} | |
var _promise = Name('deferred.promise') | |
function promise(value) { | |
var result = { | |
then: function then(onResolve, onReject) { | |
return promise(value.then(onResolve, onReject)) | |
} | |
} | |
// TODO: Find maybe better option | |
result[_promise] = value; | |
return result | |
} | |
// Deferred | |
function defer() { | |
var result = new Promise(); | |
return { | |
promise: promise(result), | |
resolve: function(value) { | |
// TODO: Find maybe better option | |
deliver(result, value && value[_promise] || value) | |
}, | |
reject: function(reason) { | |
reject(result, reason) | |
} | |
} | |
} | |
exports.defer = defer; | |
}); | |
var defer = promise.defer | |
var p1 = defer() | |
var p2 = defer() | |
p1.promise.then(console.log.bind(0, 'win'), console.log.bind(0, 'loose')) | |
p1.resolve(p2.promise) | |
p2.resolve('hello') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment