My Photo

« jQueryのAjax関数はPromiseを実装したjqXHRを返す | Main | PHPメモ038:PHPのswitchの比較は厳密な比較ではない »

April 10, 2014

Deferredで無限ループしてポーリング

以前の関連する記事:
jQueryで非同期な処理が終わるのを待つにはDeferredを使う
Deferredの復習
Deferredの復習2:jQueryのAjax関数はPromiseを実装したjqXHRを返す

「一定間隔でポーリングして処理の進行状況をチェックし、処理中ならば『処理中です』と画面に表示し、処理が終わったらポーリングをやめて『処理が完了しました』と表示させる。」というようなことをやりたい。 参考ページにDeferredを使った無限ループの実装があったので、それをベースにして考えてみた。
まず、スリープ関数とその使用例。

function mySleep(time) {
  return (
    function() {
      var dfd = $.Deferred();
      setTimeout(
        function() {
          console.log("resolve() [" + time + "sec]:" + new Date().toLocaleString());
          dfd.resolve();
        },
        time * 1000);
      return dfd.promise();
    });
}

function outputLog(msg) {
  return (
    function() {
      console.log("log: " + msg + " : " + new Date().toLocaleString());
    });
}

function testFunc() {
  $.ajax({
      url: "http://testhoge.jp/get_status.php",
      type: "POST",
      dataType: "json"
    })
  .then(
    function(data, status, xhr) {
      console.log("resolved: status=" + status + "[" + xhr.status
                  + "] data.status=" + data.status
                  + " :" + new Date().toLocaleString());
    },
    function(xhr, status, thrown) {
      console.log("rejected: status=" + status + "" + xhr.status
                  + "] :" + new Date().toLocaleString());
    })
  .then(mySleep(5))
  .done(outputLog("done"))
  .fail(outputLog("fail"));
}

上記の testFunc はリクエストが成功すると38行目の then に渡された mySleep(5) が実行された5秒間待ち、それから outputLog("done") が実行される。
リクエストが失敗すると mySleep(5) はスキップされ、ただちに outputLog("fail") が実行される。

これを踏まえて、「処理が終わるまでポーリング」の実装(script要素内)を以下に示す。

$(function() {
	polling();
});

function mySleep(time) {
  return (
    function() {
      var dfd = $.Deferred();
      setTimeout(
        function() {
          dfd.resolve();
        },
        time * 1000);
      return dfd.promise();
    });
}

// 3秒毎にポーリング
function polling() {
  $.ajax({
      // 処理の状態を取得するサービスにアクセス
      url: "http://testhoge.jp/get_status.php",
      type: "POST",
      dataType: "json"
    })
  .then(
    function(data, status, xhr) {
      // 取得したデータの内容によってDeferredの状態を変更する
      var d = $.Deferred();
      if (data.status === "completed") {
        d.reject();
      } else {
        d.resolve();
      }
      
      // Promiseを返す
      return d.promise();
    })
  .then(mySleep(3))
  .done(polling)
  .fail(
    function () {
      $("#running").hide();   // 「実行中」を非表示にする
      $("#completed").show(); // 「完了」を表示する
    });
}

初期状態で id="runnning" のdiv要素が表示されていて id="completed" のdiv要素は非表示になっている。
画面のロードが完了すると polling が実行されてリクエストが実行される。レスポンスが帰ってくると33行目の then に渡された関数が実行される。その関数はレスポンスの値が "completed" でなければresolvedなDeferredから作成したPromiseを返す。すると、次の then に渡された mySleep(3) が実行されて3秒間待った後、次の行の done に渡された polling が再帰呼び出しされる。このように3秒間隔でポーリングされる。
レスポンスが "completed" の場合はrejectedなPromiseが返されるので、mySleep(3) は実行されず、fail に渡された関数がただちに実行されて、実行中であることを示している部分を非表示にして、完了したことを知らせる要素を表示する。

別にDeferredを使わなくても setInterval() でいいような気もするが、setInterval() には指定したcallback関数の処理に時間がかかる場合に問題があるというような記事もちらほら見かける。詳しいことは setInterval() を使ったことがないのでよくわからないが。

参考ページ:
jQuery.Defferredを使ったdone無限ループとSleepで簡単ポーリング - それマグで!

« jQueryのAjax関数はPromiseを実装したjqXHRを返す | Main | PHPメモ038:PHPのswitchの比較は厳密な比較ではない »

JavaScript」カテゴリの記事

Comments

Post a comment

(Not displayed with comment.)

TrackBack

TrackBack URL for this entry:
http://app.cocolog-nifty.com/t/trackback/26461/59447848

Listed below are links to weblogs that reference Deferredで無限ループしてポーリング:

« jQueryのAjax関数はPromiseを実装したjqXHRを返す | Main | PHPメモ038:PHPのswitchの比較は厳密な比較ではない »

May 2017
Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof