Skip to content

Instantly share code, notes, and snippets.

@getify
Created December 3, 2010 17:10
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save getify/727232 to your computer and use it in GitHub Desktop.
Save getify/727232 to your computer and use it in GitHub Desktop.
theoretical native promise/defer via @ operator (in JavaScript or something like it)
//SUMMARY:
// a() @ b() ==> execute a(). if a() flags an async deferral inside it,
// then wait to continue execution of the expression until that promise is
// fulfilled, then continue execution *AT* b().
//
// more generally: X @ Y ==> evaluate X expression. if it was a function call
// call that deferred with a promise, wait until fulfilled then continue at
// Y. otherwise, assume that X had an implicit immediately fulfilled promise,
// and continue evaluating at Y.
// ------
// basically, the idea is that every function should be able to flag a "state"
// that is inspectable (by the operator) after the function's call completes,
// and that state (the "promise") indicates if the promise is immediately
// fulfilled, or if it's deferred to fulfill later. The @ operator inspects
// this special state of the function call to determine if it should wait or
// proceed with evaluation of the rest of the statement expression.
//
// NOTE: by modeling a function's promise as a special internal state of the
// function, rather than conflating it with the function's return value, the
// function can also return any value immediately, even if it's ultimately
// deferred to complete later.
function foo() {
setTimeout(function(){
blah++;
console.log(blah);
},1000);
}
var blah = 1;
foo(); blah = 10; foo(); // expected output: 11, 12
----------------
function foo() {
var p = promise; // `promise` being new auto keyword kinda like `arguments`
setTimeout(function(){
blah++;
console.log(blah);
p.fulfill();
},1000);
p.defer(); // flag this function as needing to defer its promise
}
var blah = 1;
foo() @ (blah = 10); foo(); // expected output: 2, 11
blah = 1;
foo() @ (blah = 10) @ foo(); // expected output: 2, 11
----------------
function foo() {
var p = promise; // `promise` being new auto keyword kinda like `arguments`
setTimeout(function(){
blah++;
console.log(blah);
p.fulfill();
},1000);
p.defer(); // flag this function as needing to defer its promise
}
var blah;
(blah = 5) @ foo() @ (blah = 10) @ foo(); blah = 100; // expected output: 101, 11
----------------
function foo() {
var p = promise; // `promise` being new auto keyword kinda like `arguments`
console.log(blah);
setTimeout(function(){
blah++;
console.log(blah);
p.fulfill();
},1000);
p.defer(); // flag this function as needing to defer its promise
}
var blah;
(blah = 5) @ foo(); blah = 100; // expected output: 5, 101
blah = 5; foo(); blah = 100; // expected output: 5, 101
blah = 5; foo(); blah = 100; foo(); // expected output: 5, 100, 101, 102
(blah = 5) @ foo() @ (blah = 100) @ foo(); // expected output: 5, 6, 100, 101
function foo() {
var p = promise; // `promise` being new auto keyword kinda like `arguments`
setTimeout(function(){
if (blah == 5) {
p.fulfill();
}
else {
p.fail();
}
},1000);
p.defer(); // flag this function as needing to defer its promise
}
function yay() {
console.log("Yay, blah is: "+blah);
}
function bummer() {
console.log("Bummer, blah is: "+blah);
}
var blah = 5;
foo() @ yay() : bummer(); // Yay, blah is: 5
foo() @ yay() : bummer(); blah = 10; // Bummer, blah is: 10
(blah = 5) @ foo() @ yay() : bummer(); // Yay, blah is: 5
blah = 5;
foo() @ (blah = 10) @ yay() : bummer(); // Yay, blah is: 10
blah = 5;
foo() @ (blah = 10) @ foo() @ yay() : bummer(); // Bummer, blah is: 10
blah = 5;
foo() @ ((blah = 10) @ yay() : bummer()) : bummer(); // Yay, blah is: 10
blah = 10;
foo() @ ((blah = 5) @ yay() : bummer()) : bummer(); // Bummer, blah is: 10
blah = 5;
foo() @ yay() : ((blah = 5) @ foo() @ yay() : bummer()) // Yay, blah is: 5
function foo() {
var p = promise; // `promise` being new auto keyword kinda like `arguments`
setTimeout(function(){
if (blah == 5) {
p.fulfill("`blah` was the correct value!");
}
else {
p.fail(2, "`blah` was an incorrect value.", blah);
}
},1000);
p.defer(); // flag this function as needing to defer its promise
}
function yay() {
console.log(promise.messages[0]);
}
function bummer() {
console.log(promise.messages[0]+": "+promise.messages[1]);
}
var blah = 5;
foo() @ yay() : bummer(); // `blah` was the correct value!
(blah = 10) @ foo() @ yay() : bummer(); // 2: `blah` was an incorrect value.
function onclick(obj,callback) {
obj.addEventHandler("click", callback, true);
}
var clicker = document.getElementById("clicker"),
btn = document.getElementById("btn")
;
onclick(elem,function(){
onclick(btn,function(){
console.log("clicker & button clicked");
});
});
--------------------------
function onclick(obj) {
var p = promise;
obj.addEventHandler("click", function(){
p.fulfill();
}, true);
p.defer();
}
var clicker = document.getElementById("clicker"),
btn = document.getElementById("btn")
;
onclick(elem) @
onclick(btn) @
function(){ console.log("clicker & button clicked"); };
function requestA() {
var p = promise;
// make some request remotely
// when it finishes, call p.fulfill(requestA_value);
// if it errors, call p.fail();
p.defer(); // signal this function as deferred completion
}
function requestB() {
var p = promise;
// make some request remotely
// when it finishes, call p.fulfill(requestA_value);
// if it errors, call p.fail();
p.defer(); // signal this function as deferred completion
}
--------------------------
// intersection (serial):
function intersection(A, B) {
function get_result() { return promise.messages[0]; }
var p = promise, resultA, resultB, result;
A() @ (resultA = get_result()) @ B() @ (resultB = get_result()) @ (function(){
result = resultA + resultB;
console.log(result);
p.fulfill(result);
});
p.defer();
}
intersection(requestA, requestB) @ function(){
console.log("Intersection: "+promise.messages[0]);
};
--------------------------
// intersection (parallel):
function intersection(A, B) {
function complete() {
var p = promise, result;
results.push(p.messages[0]);
if (results.length == 2) {
result = results[0] + results[1];
console.log(result);
p.fulfill(result);
}
}
var p = promise,
results = array()
;
// here, A() and B() will run in parallel, and `complete` will be the gate
A() @ complete();
B() @ complete();
p.defer();
}
intersection(requestA, requestB) @ function(){
console.log("Intersection: "+promise.messages[0]);
};
// the following is meant to explore a possible shim implementation
// (similar to how coffeescript compiles to uglier js) that could
// emulate the proposed @ behavior in current JS.
X @ Y
when(X, function() { Y; });
// but what if Y relies on the `this` or `arguments`?
function do_Y() {
Y;
}
var args = Array.prototype.slice.call(arguments);
args.unshift(this);
var bound_Y = do_Y.prototype.bind.apply(this,args);
when(X, bound_Y);
// what about if Y uses a `return`? Too complicated. I'd
// simply say the semantics of @ expressions are that the
// `rvalue` cannot be a `return` statement/expression.
// Because @ can suspend the containing statement, it can be used in control and loop structures, as well
if (X() @ true) {
// if X() completes immediately, this block will run
// if X() defers, the whole if-statement is paused.
// if X() eventually fulfills successfully, the @ expression
// will evaluate the `true`, and this block will then run
// if X() eventually fails, the @ expression will evaluate to
// `undefined` (falsy), and this block will NOT run
}
// OR, more explicitly if you like
if (X() @ true : false) {
// will be have the exact same way
}
// in a loop:
for (i=0; X() @ true; i++) {
// loop iteration each time that X() fulfills successfully,
// whether that's immediate or later
// stop when X() fails
}
@getify
Copy link
Author

getify commented Dec 7, 2010

Here's the follow-up blog post continuing the discussion: http://blog.getify.com/2010/12/native-javascript-sync-async/

And here's the discussion thread on "es-discuss" list: https://mail.mozilla.org/pipermail/es-discuss/2010-December/012278.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment