JSDeferred Sample

About JSDeferred: CodeRepos Wiki

Deferred is able to write async code cleanly.


click to execute the code.

Export deferred functions.

// export functions to global;
Deferred.define();

// export to aObj
// Deferred.define(aObj);

// export specic functions
// var global = (function () { return this })();
// Deferred.define(global, ["next"]);

// full name
Deferred.next(fun);

Basic Chain

next(function () {
	print("start");
}).
next(function () {
	function pow (x, n) {
		function _pow (n, r) {
			print([n, r]);
			if (n == 0) return r;
			return call(_pow, n - 1, x * r);
		}
		return call(_pow, n, 1);
	}
	return call(pow, 2, 10);
}).
next(function (r) {
	print([r, "end"]);
}).
error(function (e) {
	alert(e);
})

function print (m) { $("#basic-code").append("\n// "+m) }

Basic Loop

This loop is slow but not blocking browsing.

loop(10, function (i) { print(i) });
function print (m) { $("#basic-loop-code").append("\n// "+m) }

Deferred Ajax

$.get("sample.html").next(function (data) {
	$("#ajax-code").append("\n// "+data.length);
});

Parallel Deferred

DeferredList

print("start. gathering data.");

parallel([$.get("sample.html"), $.get("jsdeferred.js")]).
next(function (values) {
	var lengths = $.map(values, function (i) { return i.length });
	print(lengths.join(", "));
});

function print (m) { $("#parallel-code").append("\n// "+m) }
print("start. gathering data.");

// named parallel deferred
parallel({html: $.get("sample.html"), js: $.get("jsdeferred.js")}).
next(function (values) {
	print(["html=", values.html.length, " js=", values.js.length].join(""));
});

function print (m) { $("#parallel-code2").append("\n// "+m) }
print("start. wait 3 sec.");

var list = [];
var printAndReturn = function (i) { print(i+"msec elapsed"); return i; };
list.push(wait(0).next(printAndReturn));
list.push(wait(1).next(printAndReturn));
list.push(wait(2).next(printAndReturn));
list.push(wait(3).next(printAndReturn));

parallel(list).next(function (values) {
	print("Completed. values: "+values.join(", "));
});

function print (m) { $("#parallel-code1").append("\n// "+m) }

Workers

var queue   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var workers = new Array(2);
var work    = function (job) {
	print('working... ' + job);
	return wait(3);
};

for (var i = 0, len = workers.length; i < len; i++) {
	workers[i] = next(function me () {
		var job = queue.shift();
		if (!job) return;

		print("start worker: " + job);
		return next(function () { return job }).
		next(work).
		next(me);
	}).
	error(function (e) {
		alert(e);
	});
}

parallel(workers).next(function () {
	print('all done!');
});

function print (m) { $("#worker-code").append("\n// "+m) }

Divided Loop

next(function () {
	var sum = 0;
	return loop({end:100000, step:1000}, function (n, o) {
		print(["Processing divided loop:n=", n, ", sum=", sum, " last?=", o.last].join(""));
		for (var i = 0; i < o.step; i++) {
			// print(i + n);
			sum += i + n;
		}
		print(["sum=", sum].join(""));
		return sum;
	});
}).
next(function (e) {
	print("Result:"+e);
	print("end");
}).
error(function (e) {
	print(e);
});


function print (m) { $("#loop-code").append("\n// "+m) }
loop({begin: 1, end:100, step:10}, function (n, o) {
	print(["Processing divided loop:n=", n, " last?=", o.last].join(""));
	for (var i = 0; i < o.step; i++) {
		var j = n + i;
		print(j);
	}
});

function print (m) { $("#loop-code1").append("\n// "+m) }

Auto Divided Loop

function aloop (n, f) {
	var i   = 0;
	var end = new Object;
	var ret = null;
	return Deferred.next(function () {
		var t = (new Date()).getTime();
		try {
			do {
				ret = f(i)
				i++;
				if (i >= n) throw end;
			} while ((new Date()).getTime() - t < 50);
			print("Devided: " + ((new Date()).getTime() - t) + "msec.");
			return Deferred.call(arguments.callee);
		} catch (e) {
			if (e == end) {
				print("End");
				return ret;
			} else {
				throw e;
			}
		}
	});
}

aloop(100, function (n, o) {
	print(n);
	for (var i = 0; i < Math.pow(n, 2); i++) {
		for (var j = n; j; j--);
	}
});


function print (m) { $("#aloop-code").append("\n// "+m) }

Pseudo Multi Thread

loop(10, function (n) {
	print(n);
	return wait(0.1);
});

loop(10, function (n) {
	print(String.fromCharCode(97+n));
	return wait(0.2);
});

function print (m) { $("#thread-code").append(" "+m) }

//

Shorthand

print(0);
loop(10, function (n) {
	print(n);
	return n;
}).
wait(1).
loop(10, function (n) {
	var c = String.fromCharCode(97+n);
	print(c);
	return c;
}).
next(function (i) {
	print("end");
}).
error(function (e) {
	alert(e);
});

function print (m) { $("#shorthand-code").append(" "+m) }

//

Tiny Effects

function effect (o) {
	var start = new Date().valueOf();
	var curr  = +o.target.style[o.name].match(/\d+/);
	return next(function () {
		var rate = (new Date().valueOf() - start) / o.time;
		if (rate > 1) rate = 1;
		o.target.style[o.name] = (curr + rate * o.value) + "px";
		if (rate < 1) {
			return call(arguments.callee);
		}
	});
}
// register function to use shorthand of chain
$.deferred.register("effect", effect);

var ele  = document.getElementById("effect-code1");
effect({
	target : ele,
	name   : "left",
	value  : 100,
	time   : 300
}).
effect({
	target : ele,
	name   : "top",
	value  : 100,
	time   : 300
}).
next(function () {
	return parallel([
		effect({
			target : ele,
			name   : "left",
			value  : -100,
			time   : 300
		}),
		effect({
			target : ele,
			name   : "top",
			value  : -100,
			time   : 300
		})
	]);
}).
error(function (e) {
	alert(e);
});

Delay Loop

loop(5, function (i, o) {
	print(i);
	return o.last? i : wait(1);
}).
next(function (e) {
	print("end ["+e+"]");
}).
error(function (e) {
	print(e);
});


function print (m) { $("#delay-loop-code1").append("\n// "+m) }
next(function (i) {
	function delayloop (i) {
		print(i++);
		if (i < 5) {
			return wait(1).next(function () {
				return call(delayloop, i);
			});
		}
	}
	return call(delayloop, 0);
}).
next(function (e) {
	print("end");
}).
error(function (e) {
	print(e);
});


function print (m) { $("#delay-loop-code").append("\n// "+m) }

Step Run (event handling)

(double click to run this code and click to step)

var deferred = Deferred();
$("#step-run").click(function () { deferred.call() });

loop(5, function (i) {
	print("running... " + i);
	return deferred;
}).
next(function () {
	print("completed");
});

function print (m) { $("#step-run").append("\n// "+m) }

Brainfuck interpreter

No wait but toooooooo slow :( But, but, browser will not be stopped!

function bfrun () {
	var mem    = $("<pre/>"), out = $("<pre/>"), button = $("#bfi-run").hide();
	var sinput = $("#bfi-source").hide(), source = sinput.val();
	var shtml  = $("<div class='code'/>");
	var selems = $.map(source.split(/\n/), function (l) {
		var line = $("<div class='line'/>").appendTo(shtml);
		return $.map(l.split(""), function (c) {
			return $("<span/>").append(c).appendTo(line);
		})
	});
	sinput.before(mem).before(out).before(shtml);

	var pi = {
		"+": function (c) {
			if (!c.stack[0]) return c;
			c.memory[c.pointer]++;
			return c;
		},
		"-": function (c) {
			if (!c.stack[0]) return c;
			c.memory[c.pointer]--;
			return c;
		},
		">": function (c) {
			if (!c.stack[0]) return c;
			c.pointer++;
			c.memory[c.pointer] = (c.memory[c.pointer] || 0);
			return c;
		},
		"<": function (c) {
			if (!c.stack[0]) return c;
			c.pointer--;
			c.memory[c.pointer] = (c.memory[c.pointer] || 0);
			return c;
		},
		".": function (c) {
			if (!c.stack[0]) return c;
			c.output.push(String.fromCharCode(c.memory[c.pointer]));
			return c;
		},
		",": function (c) {
			if (!c.stack[0]) return c;
			c.memory[c.pointer] = "t";
			return c;
		},
		"[": function (c) {
			if (c.memory[c.pointer] == 0) {
				c.stack.unshift(false);
			} else {
				c.stack.unshift(c.pos - 1);
			}
			return c;
		},
		"]": function (c) {
			var s = c.stack.shift();
			if (s) c.pos = s;
			return c;
		}
	};

	next(function () {
		return {
			pos     : 0,
			pointer : 0,
			memory  : [0],
			stack   : [true],
			output  : [],
			source  : source
		};
	}).
	next(function (c) {
		if (selems[c.pos]) selems[c.pos].removeClass("em");
		var chr, fun;
		do {
			chr = c.source.charAt(c.pos);
			fun = pi[chr];
			c.pos++;
		} while (chr.match(/[^<>,.\[\]+-]/));
		var m = c.memory.concat();
		m.splice(c.pointer, 1, "*"+(c.memory[c.pointer] || 0));
		mem.text(m.join(", "));
		out.text(c.output.join(""));

		if (typeof fun == "function") {
			c = fun(c);
			if (c.pos < c.source.length) {
				if (selems[c.pos]) selems[c.pos].addClass("em");
				return call(arguments.callee, c);
			} else {
				return c;
			}
		} else {
			return c;
		}
	}).
	next(function (c) {
		var reset = $("<button>Reset</button>")
		sinput.after(reset);
		reset.click(function () {
			sinput.show();
			button.show();
			mem.remove();
			out.remove();
			shtml.remove();
			reset.remove();
		});
		log("end");
	}).
	error(function (e) {
		alert(e);
	});

	function log (m) {
		// console.log(uneval(m));
	}
}

callcc

like Scheme's callcc

function callcc (fun) {
	var error = new Deferred();
	return call(function () {
		// JSDeferred passes current Deferred Object to  this.
		var ccdeferred = this;
		// Call with current continuation (calling Deferred.next)
		return fun(function (a) { ccdeferred._next.call(a); throw error });
	}).
	error(function (e) {
		// Current Deferred chain must be stopped
		if (e === error) {
			return e;
		} else {
			throw e;
		}
	});
}

callcc(function (cont) {
	return 10 * 10 * cont(20);
}).
next(function (val) {
	print("callcc1 returns:" + val);
});
// should show "callcc1 returns:20"

var cont;
var i = 0;
callcc(function (c) {
	cont = c;
	return 10;
}).
next(function (val) {
	print("callcc2 returns:" + val);
	if (!i++) cont(20);
});
// should show "callcc2 returns:10", "callcc returns:20"


function print (m) { $("#callcc").append("\n// "+m) }

and Scheme's amb

function callcc (fun) {
	var error = new Deferred();
	return call(function () {
		// JSDeferred passes current Deferred Object to  this.
		var ccdeferred = this;
		// Call with current continuation (calling Deferred.next)
		return fun(function (a) { ccdeferred._next.call(a); throw error });
	}).
	error(function (e) {
		// Current Deferred chain must be stopped
		if (e === error) {
			return e;
		} else {
			throw e;
		}
	});
}

// http://www.sampou.org/scheme/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14
// 上記のものをそのまま移植したもの
function amb () {
	var alts = arguments;
	var prevAmbFail = amb.ambFail;

	return callcc(function (sk) {
		return loop(alts.length, function (i) {
			var alt = alts[i];
			return callcc(function (fk) {
				amb.ambFail = function () {
					amb.ambFail = prevAmbFail;
					return fk("fail");
				};
				return sk(alt);
			});
		}).
		next(prevAmbFail);
	});
}
amb.ambFail = function () { throw "amb tree exhausted" };

// Utility function
function amb1 (ambvars) {
	var f    = wait(0);
	var vars = {};
	for (var k in ambvars) if (ambvars.hasOwnProperty(k)) (function (name, val) {
		log(name);
		f = f.next(function () {
			return amb.apply(this, val).next(function (i) {
				vars[name] = i;
				return vars;
			});
		});
	})(k, ambvars[k]);

	return f;
}

function assert (cond) {
	if (!cond) throw amb();
}

// http://mayokara.info/note/view/251
Array.prototype.uniq = function(){
	for (var i = 0,l = this.length; i < l; i++) {
		if (this.indexOf(this[i]) < i) {
			this.splice(i--, l-- && 1);
		}
	}
	return this;
};

amb1({
	baker    : [1, 2, 3, 4, 5],
	cooper   : [1, 2, 3, 4, 5],
	fletcher : [1, 2, 3, 4, 5],
	miller   : [1, 2, 3, 4, 5],
	smith    : [1, 2, 3, 4, 5]
}).
next(function (vars) { with (vars) {
	log(vars);
	// 簡易 distinct
	assert([baker, cooper, fletcher, miller, smith].uniq().length == 5);
	log("distinct passed");
	assert(baker  != 5);
	assert(cooper != 1);
	assert(fletcher != 1 && fletcher != 5);
	assert(miller > cooper);
	assert(Math.abs(smith - fletcher)  != 1);
	assert(Math.abs(fletcher - cooper) != 1);

	return vars;
} }).
next(function (vars) { with (vars) {
	log("solved");
	log(vars);
	alert(uneval(vars));
} }).
error(function (e) {
	alert(e)
});

function log (m) { $("#amb").append("\n// "+ uneval(m)) }