非同期の処理をうまく扱うためにDefferdをつくりました。
要件
- 内部状態は 初期状態 から 成功 / 失敗 に切り替わる
- 1度切り替わったら状態は変わらない
- 成功、失敗状態になった際に発火するコールバック関数を登録することができる
- コールバック関数は登録した順に発火する
JavaScript
(function(win, doc) { "use strict"; function Deferred() { this.success = new SimpleDeferred(); this.miss = new SimpleDeferred(); function SimpleDeferred() { this.queue = []; this.arg = null; this.isReject = false; } SimpleDeferred.prototype.done = done; SimpleDeferred.prototype.resolve = resolve; SimpleDeferred.prototype.reject = reject; function done(callback) { if (this.isReject) { return; } if (!!this.queue) { this.queue.push(callback); } else { callback.apply(this, this.arg); } } function resolve(opt_arg) { var length = this.queue.length, i; if (this.isReject) { return; } this.arg = (typeof opt_arg !== "undefined") ? arguments : null; for (i = 0; i < length; i++) { this.queue[i].apply(this, this.arg); } this.queue = null; } function reject() { if (!!this.queue) { this.isReject = true; } } } Deferred.prototype.done = done; // 成功した祭のコールバックを登録 Deferred.prototype.fail = fail; // 失敗した際のコールバックを登録 Deferred.prototype.resolve = resolve; // 成功状態に切り替え Deferred.prototype.reject = reject; // 失敗状態に切り替え function done(callback) { this.success.done(callback); } function fail(callback) { this.miss.done(callback); } function resolve(opt_org) { this.success.resolve(opt_org); this.miss.reject(); } function reject(opt_org) { this.success.reject(); this.miss.resolve(opt_org); } win.Deferred = Deferred; })(this, document);
検証
var dfd = new Deferred(); setTimeout(function() { console.log("done"); dfd.done(function(eve) { // 成功時のコールバック登録 alert(eve); }); }, Math.random() * 1000); setTimeout(function() { // 失敗時のコールバック登録 console.log("fail"); dfd.fail(function(evt) { alert(evt); }); }, Math.random() * 1000); setTimeout(function() { // 状態を成功に切り替え console.log("resolve"); dfd.resolve("OK"); }, Math.random() * 1000); setTimeout(function() { // 状態を失敗に切り替え console.log("reject"); dfd.reject("NG"); }, Math.random() * 1000);
登録、状態変化がどんな順番になっても成立することがわかります。