かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

javascript setTimeoutで関数内から自身を呼び出す時に初回を即時関数にするとうまく動作しない

javascriptAPIを叩く関数を作ってAPIからデータ取得後にsetTimeoutを使って同じ関数を呼びたいような時 次のような関数を作ったりします。

var timer;
// AjaxでAPIを叩いてデータを取得する関数
var getMyAPIData = function() {
  clearTimeout(timer);
  $.ajax(
    url: '/myapi',
    dataType: 'json',
    type: 'get',
    data: postData
  )
  .done( function( res ) {
    // データが取得できた時の処理
  } )
  .fail( function( jqXHR, statusText, errorThrown ) {
    // データ取得に失敗した時の処理
  } )
  .always( function() {
    // 1分後後にAPIからデータを取得する
    timer = setTimeout( function() {
      getMyAPIData();
    }, 60000 );
  } );
};

とりあえず、どこかでgetMyAPIData()を呼び出せば後はAjaxの通信完了から1分後にgetMyAPIData()が呼び出されるようになります。
この時、初回の関数の呼び出しを直ぐに実行させたいからと即時関数にしてしまうとgetMyAPIDataundefinedになるのでうまく動作しません。

ダメな例

var timer;
// AjaxでAPIを叩いてデータを取得する関数
var getMyAPIData = function() {
  clearTimeout(timer);
  $.ajax(
    url: '/myapi',
    dataType: 'json',
    type: 'get',
    data: postData
  )
  .done( function( res ) {
    // データが取得できた時の処理
  } )
  .fail( function( jqXHR, statusText, errorThrown ) {
    // データ取得に失敗した時の処理
  } )
  .always( function() {
    // 1分後後にAPIからデータを取得する
    timer = setTimeout( function() {
      getMyAPIData(); // getMyAPIDataが`undefined`なのでエラーになる
    }, 60000 );
  } );
}();  // 初回の呼び出しを即時関数で実行

console.log( getMyAPIData ); // undefined

関数を記述した後にconsole.log(getMyAPIData)をしてもgetMyAPIDataundefinedになっているので、即時関数だからalwaysの中で呼び出される変数getMyAPIDataがまだ定義されていない訳ではなく、そもそもundefinedになってしまうのが原因なのです。

returnしてないので当たり前といえば、当たり前なのですが、忘れているとハマります。
仮に関数の最後でreturn this;としてもthiswindowなので期待した結果にはならないかと思います。関数である自身を返すようにするのは少し面倒くさいので素直に関数定義後に関数を呼んであげるのがカンタンかなとおもいます。

c.f