[JavaScript] jQuery.Deferredで登録時に引数を持たせたい

2013/05/24

こんにちは。きんくまです。

今回はjQuery.Deferredです。

これは、非同期処理をわりと簡潔に書けるようになるので、ソースコードがあっちにいったり、こっちにいったりしないで、見やすくなるのが便利ポイントかなと思いました。

ASでもCommand系のライブラリがあったので、使用感としてはそれに近い感じでしょうか。並列、直列の非同期を書けるところとか。

で、わかりやすいチュートリアル記事があったので、これで勉強デス。
>> 爆速でわかるjQuery.Deferred超入門

ためしに書いたタイマーの非同期処理

var myTimer = function(){
	var d = new $.Deferred();
	setTimeout(function(){
		console.log('timer complete');
		d.resolve();
	}, 1000);
	console.log('timer start');
	return d.promise();
}

myTimer()
.then(myTimer)
.then(function(){
	console.log('all tasks complete');
});

これやってて、例えば登録時(.then)に引数をもたせたいことって結構あると思います。
3秒待ってから、5秒待ってからとか。

なので、ためしに引数をもたせてみてもうまくいかないのですよね。
登録時に実行してしまって、関数を登録していないのが問題。

var myTimer = function(delayTime){
	var d = new $.Deferred();
	setTimeout(function(){
		console.log('timer complete');
		d.resolve();
	}, delayTime * 1000);
	console.log('timer start');
	return d.promise();
}

myTimer(2)
.then(myTimer(3))
.then(function(){
	console.log('all tasks complete');
});

また.thenに登録する関数の引数は実行時に前の.promise()の中に入る引数が入っているということもあります。
日本語よくわからないと思うので、コードで説明

var myTimer = function(){
	var d = new $.Deferred();
	setTimeout(function(){
		d.resolve('next message');
	}, 1000);
	console.log('timer start');
	return d.promise();
}

myTimer()
.then(function(msg){
	console.log(msg);
});

前のタイマーのresolveで入れた文字列が、次の.thenの中のmsgに入っています。
今回は文字列いれましたけど、配列でもObjectでも何でも入れて大丈夫です。

で、引数つきの関数の件は最終的に

var myTimer = function(delayTime){
	return function(){
		var d = new $.Deferred();
		setTimeout(function(){
			console.log('timer complete');
			d.resolve();
		}, delayTime * 1000);
		console.log('timer start delay ' + delayTime);
		return d.promise();
	}
}

とかやっておけば大丈夫でした。

ためしに、画像読み込みなども作ってみました。これで引数つけて登録できるので便利かなと。

var asyncLoadImage = function(path){
	return function(){
		var d = new $.Deferred();
		var img = new Image();
		img.onload = function(){
			console.log('load image complete');
			d.resolve();
		}
		img.src = path;
		console.log('start load image');
		return d.promise();
	}
}

var myTimer = function(delayTime){
	return function(){
		var d = new $.Deferred();
		setTimeout(function(){
			console.log('timer complete');
			d.resolve('resolved message');
		}, delayTime * 1000);
		console.log('timer start delay ' + delayTime);
		return d.promise();
	}
}

var logMsg = function(firstMsg){
	return function(resolvedMsg){
		console.log(resolvedMsg);
		console.log(firstMsg);
	};
}

myTimer(2)()
.then(logMsg('hello'))
.then(asyncLoadImage('images/sample1.gif'))
.then(myTimer(3))
.then(function(){
	console.log('all tasks complete');
});

で考えてみると、.then()のときに.promise()を返す関数を登録すればよいので、クラスでもふつうに使えます。

var ImageLoader = function(path){
	this.deferredObj;
	this.path = path;
	this.init();
};
ImageLoader.prototype = {
	init:function(){
		this.deferredObj = new $.Deferred();
	},
	loadImage:function(){
		var self = this;
		var img = new Image();
		img.onload = function(){
			console.log('load complete');
			self.deferredObj.resolve();
		};
		img.src = this.path;
		console.log('load start');
		return this.deferredObj.promise();
	}
}

//myTimerはひとつ前のコードから拝借したと思ってください。
myTimer(2)()
.then(function(){
	var loader = new ImageLoader('images/sample1.gif');
	return loader.loadImage();
})
.then(function(){
	console.log('all tasks complete');
})

これ使って、よくFlashのときにやってた、FadeInとかのTween系とか、画像や外部テキストの読み込み処理を、クラス化したりすると便利かなーと思いました。

LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る