追記
Deferredチェーン、非同期処理の逐次実行 - oct inaodu
↑ここで解説されてるDeferredチェーンが数分で考えたgotin方式なんかより全然洗練されてますね。多段になるように改造すれば同じような感じになるんかな。。
本文
JavaScript tailcall... - 冬通りに消え行く制服ガールは✖夢物語にリアルを求めない。 - subtech
↑これは使えますね。
ここではプロセス順をうまく制御して末尾再帰をsetTimeoutで処理するようにしてるところがポイントだと思います。
で、あんまりこれのポイントとは関係がないんですが、GM_xmlhttpRequestだと同期処理ができないけど、同期処理してるみたいに書けないかなぁというのを、↑これをちょっと改造して書いてみました。
// ==UserScript== // @name process_test // @namespace gomaxfire.dnsdojo.com // @include * // ==/UserScript== function Process () { this.init.apply(this); } Process.prototype = { init: function () { var self = this; this.stack = []; this._rvalue = undefined; this.pause = false; // trueだと処理を進めない setTimeout(function () { self.call() }, 0); }, push:function(f){ // push(というかcall)したときpause状態なら再開するように this.stack.push(f); if(this.pause){ var self = this; setTimeout(function () { self.call() }, 0); } }, p: function (f) { this.stack.unshift({f:f, go:true}); //goで処理を進めるかどうかを制御 return this; }, pstop: function (f) { this.stack.unshift({f:f, go:false}); //pstopだと実行後pause状態に return this; }, call: function () { var self = this; var pr =this.stack.pop(); var f = pr.f; var thisObj = {}; thisObj.call = function (f, args) { args = Array.prototype.slice.call(arguments); f = args.shift(); self.push({ f:function () { return f.apply(thisObj, args); }, go:true }); return "this"; }; this._rvalue = f.call(thisObj, this._rvalue); if (this.stack.length) { if(pr.go){ setTimeout(function () { self.call() }, 0); } else { self.pause = true; // pstopの場合はpause状態に } } } }; function log(m){console.log(m);} var ps = new Process(); ps.p(function(){ log("start"); }).pstop(function(){ // GM_xmlhttpRequestの実行後pause状態に log("xhr start"); var self = this; GM_xmlhttpRequest({ url:document.location.href, method:"GET", onload:function(xhr){ self.call(function(){return xhr}); //もどってきたら再開(xhrを次の処理に渡す) } }); }).p(function(xhr){ log(xhr.responseText); // ↑で返されるxhrをつかって処理 log("end"); });
結果:
start xhr start (開いているページのソース) end
目的は達成できてるっぽいけど穴はありそう。もっと美しくやるほうほうもあるかな。