備忘録

Windows,Linux,Mac,AWS,VMware,ネットワークなどの検証

setTimeoutをタイマーとして使う/同期的に使う

約1秒(1000ミリ秒)ごとに、「Hello」と「world!」を表示させるには、次のようにする。

※Node.js v8.9.3 および Google Crome v63.0.3239.132 にて動作確認

async / await を使った場合

// 1秒待ったあと、Hello と表示する関数
function hello(){
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      console.log('Hello');
      resolve();
    }, 1000);
  });
}

// 1秒待ったあと、world! と表示する関数
function world(){
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      console.log('world!');
      resolve();
    }, 1000);
  });
}

// 関数hello と関数world を続けて実行する。
async function myFunc(){
  await hello();
  await world();
}
myFunc();
// 1秒待機
// Hello
// 1秒待機
// world!

promise を使った場合

// 1秒待ったあと、Hello と表示する関数
function hello(){
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      console.log('Hello');
      resolve();
    }, 1000);
  });
}

// 1秒待ったあと、world! と表示する関数
function world(){
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      console.log('world!');
      resolve();
    }, 1000);
  });
}

// 関数hello と関数world を続けて実行する。
hello()
  .then(world); 
// 1秒待機
// Hello
// 1秒待機
// world!

動作は、

  1. hello() を実行
  2. hello関数内のresolve()が実行され、次の関数(ここではworld())が実行される。

カギとなるのは、setTimeout内のresolve()。 これが実行されると、thenで指定された次の関数が実行されます。

setTimeout について

setTimeoutは、以下のように記述します。

setTimeout(実行する関数, 待機ミリ秒);

例: 1秒後にHello world! と表示する
setTimeout(()=>{   //無名関数を使用している
  console.log('Hello world!');
}, 1000);

とすると、上記の例は以下のように書けると思われる。

// 1秒待ったあと、Hello と表示し、続けて
// 1秒待ったあと、world! と表示する関数
// *** この実装では思ったようには動かない *** 
function Helloworld(){
  setTimeout(()=>{
    console.log('Hello');
  }, 1000);

  setTimeout(()=>{
    console.log('world!');
  }, 1000);
}

Helloworld(); 
// 期待する動作
// Hello
// world!  // 1秒後

しかしこの場合、Hello とworld は、コード実行の約1秒後に、同時に表示される。 これは、次のような動きをしているからである。

  1. Helloworld() を実行
  2. 最初のsetTimeoutが、1秒後に実行されることを予約
  3. 1秒を待たずに次の処理に移る
  4. 2番目のsetTimeoutが、1秒後に実行されることを予約
  5. 1秒を待たずに次の処理に移る。処理が無いので動作を終了する。
  6. 予約された2つの setTimeoutが、1秒後に実行される

待機するよう設定された時間を待たずに次の処理へ移る、このような動作を「非同期的」という。

非同期的な動きをするsetTimeoutを、順に実行させたい(同期的に動作させたい)場合、 promiseasync / await といった、非同期動作を制御する仕組みを使用する必要がある。

参考資料