Skip to content

Instantly share code, notes, and snippets.

@Gozala
Created May 21, 2012 17:23
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Gozala/2763402 to your computer and use it in GitHub Desktop.
Experimenting with promises.
/* 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