みかづきブログ その3

3ヶ月つづけてみました。

Deferredをつかって非同期の処理をうまく処理する

非同期の処理をうまく扱うためにDefferdをつくりました。


要件

  1. 内部状態は 初期状態 から 成功 / 失敗 に切り替わる
  2. 1度切り替わったら状態は変わらない
  3. 成功、失敗状態になった際に発火するコールバック関数を登録することができる
  4. コールバック関数は登録した順に発火する

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);

登録、状態変化がどんな順番になっても成立することがわかります。