2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Promiseは毎回受け取らないといけない

Last updated at Posted at 2018-10-05

はじめに

Promiseで非同期を順番に実行する場合は以下のようにすると思います。

普通にチェーンで書く
Promise.resolve()
  .then(() => {
    return new Promise((resolve) => {
      window.setTimeout(() => {
        console.log('1');
        resolve();
      }, 1000);
    });
  })
  .then(() => {
    console.log('2');
  });

基本的にはチェーンで書けば問題ないですが、ループで書かなくちゃいけなくなった場合はチェーンで書けないので、うまく書かないといけません。

失敗例

失敗例が以下のようになります。Promiseオブジェクトさえ持って入れば問題ないと思っていたのでPromise.resolve()したあと順番にセットしていけば裏でよしなにやってくれるものだと思っていました。

失敗例
const promise = Promise.resolve();
// 1回設定して
promise
  .then(() => {
    return new Promise((resolve) => {
      window.setTimeout(() => {
        // 最初に1を出力
        console.log('1');
        resolve();
      }, 1000);
    });
  });

// また設定する
promise
  .then(() => {
    // 次に2を出したい
    console.log('2');
  });

実際は2が先に出てしまいました。どうやら2を出す方もPromise.resolve()が解決した時の設定になったようです。この挙動は逆に言えばあるPromiseが解決した時に並列して実行することができそうですね(並列実行はPromise.allでいいですが)

実行結果
2
1

成功例

チェーンでうまくいっていたのは、毎回新しいPromiseオブジェクトを受け取っていたからのようです。
なので、毎回promiseを受け取っておけばきちんと動作します。

成功例
let promise = Promise.resolve();
// キチンと実行結果を受け取る
promise = promise
  .then(() => {
    return new Promise((resolve) => {
      window.setTimeout(() => {
        console.log('1');
        resolve();
      }, 1000);
    });
  });
promise
  .then(() => {
    console.log('2');
  });
実行結果
1
2

ループを綺麗に書く

上でキチンと動くようになりましたが、毎回代入しないといけなくて、あんまりいい感じがしません。
これを解決する方法として、reduceがあります。第一引数がreturnされたものになるため、毎回Promiseを更新するのにちょうどいいです。

reduceでPromiseの直列処理を綺麗に書く
[1, 0].reduce((promise, waitTime, index) => {
  return promise
    .then(() => {
      return new Promise((resolve) => {
        window.setTimeout(() => {
          console.log(index + 1);
          resolve();
        }, waitTime * 1000);        
      });
    });
}, Promise.resolve());

終わりに

thenでPromiseが返ってくるのは知っていましたが、チェーンで書くためにただ返していたものだと思っていました。実際は新しいPromiseを返していて意外と引っかかったので記事にしました。
一応サンプルコードも以下に置いておきました。画面には何も表示されないのでターミナルで見てもらえると幸いです。
https://jsfiddle.net/wintyo/6bvrsx1w/23/

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?