0
0

ややこしいPromiseの"resolve"を整理して理解する

Posted at

はじめに

「Promiseと一緒によく出てくるやつTOP3」には入るであろう"resolve"(当社調べ)。
しかしこの"resolve"は文脈によって異なるものを指している場合があり、なんと最大4つの概念を含むことが明らかになりました(当社調べ)。
私自身もPromiseを理解するにあたってこの"resolve"という概念に非常に苦しめられたことがありますし、"resolve"がよく分からなくて避けていた側面もあります。
そこで、今回はこの"resolve"をできるだけわかりやすく整理していきます。

目次

  1. "resolve"の4分類
  2. Promiseを「解決」する
  3. Promiseの"Resolved"というfate
  4. Promise.resolve()
  5. コンストラクタの中のresolve関数
  6. まとめ

"resolve"の4分類

早速ですが"resolve"という言葉が指しうる概念は、以下の4つになります

  • Promiseを「解決」するということ
  • Promiseの"resolved"というfate
  • Promise.resolve()というPromiseオブジェクトの静的メソッド
  • Promiseオブジェクトのコンストラクタ内で用いられる関数

ではそれぞれ順番に見ていきましょう。

Promiseを「解決」する

これが最も多い使い方なのではないでしょうか。
"resolve"は「解決する」という動詞です。
よって、単純にPromiseを解決するときの「解決」の意味で使われます。
Promiseインスタンスは3つの State(状態) を持ち、それぞれ

  • Pending(待機状態)
  • Fulfilled(履行状態)
  • Rejected(拒否状態)

があります。
例えばまだ何の操作もされていないPromiseインスタンスはPending状態です。
このPromiseインスタンスを「解決」すると、Fulfilled状態になって値を取り出したり、続くthen()のコールバック関数の処理などが可能になるわけです。
この解決する操作のことを指して"resolveする"などと言ったりします。

Promiseの"Resolved"というfate

正直これは知らなくてもそんなに影響ないし、ほぼ使われないですが一応、 PromiseにはStateによく似た概念として、Fateというものがあります。
States and Fatesという記事の中では、

Fates

Promises have two possible mutually exclusive fates: resolved, and unresolved.

  • A promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected.
  • A promise is unresolved if it is not resolved, i.e. if trying to resolve or reject it will have an impact on the promise.

とありますね。まあなんのこっちゃって話ですが、要するに、

  • 履行や拒否を試みても何も結果が変わらないPromiseインスタンスをresolvedという。つまりSettledのPromiseインスタンスに加え、Promise chain内のthen()など他のPromiseインスタンスによって自身のStateが決められるPromiseインスタンスである
  • ResolvedでないPromiseインスタンスをunresolvedという

ということです。
このFateの文脈でもresolvedという"resolve"と混同しそうな概念が出てきます。

then()など他のPromiseインスタンスによって自身のStateが決められるPromiseインスタンス

という書き方をしていますが、then()は前のPromiseインスタンスとは別の新たなPromiseインスタンスを返しているということに注意してください。
つまり、Promise.resolve().then(callback)においてthen(callback)の返すPromiseインスタンスは、その前にあるPromise.resolve()によってStateが決められるのでFateがresolvedであるという考え方をします。

Promise.resolve()

続いてはPromiseの静的メソッドであるresolveメソッドです。
適当なPromiseインスタンスの中を除くと存在が確認できます。

promseResolvingFromMethod.js
console.log(Promise.resolve());
Promise {<fulfilled>: undefined}
    [[Prototype]]: Promise
        catch: ƒ catch()
        constructor: ƒ Promise()
            all: ƒ all()
            // 省略
            reject: ƒ reject()
            resolve: ƒ resolve()
            // 以下省略

ありましたね。constructorの中に入っている、つまりresolveメソッドはPromiseオブジェクトの静的メソッドであることが確認できました。
このように全てのPromiseインスタンスがもつ、PromiseインスタンスをFulfilledにする(しようと試みる)メソッドが resolveメソッド であり、その文脈で"resolve"という言葉が使われることもありそうですね。

コンストラクタの中のresolve関数

最後がPromiseオブジェクトのコンストラクタ内で用いられるresolve関数です。そもそもPromiseオブジェクトのコンストラクタは以下のようになるのでした。

promiseResolvingFromExecutor.js
const promise = new Promise((resolve, reject) => {
    resolve("解決!")
})
promise.then(data => console.log(data));
解決!

このようにPromiseコンストラクターのコールバック関数において、宣言したPromiseオブジェクトを、resolveという関数で履行することができます。ちなみに、Promiseコンストラクターのコールバック関数のことをPromise Exexutorと言います。
Promise Executorの第一引数にはそのPromiseを履行する際の処理を、第二引数にはPromiseを拒否するときの処理を渡します。なお、第二引数は省略可能です。

なお、Promise Executorで引数に渡すresolve関数は、あくまで慣例としてresolveという名前が付けられることが多いだけで、"res"などの他の名前を付けても動作します。
本質はPromise Executorの第一引数がPromiseをFulfilledにする関数であるということです。その関数の名前は問題ではありません。

Promise Executorの(慣例としての)resolve関数の文脈でも"resolve"という言葉が用いられるというわけです。

まとめ

以上のように、意外とPromiseの"resolve"という言葉はいろいろな文脈で用いられますし、それが混乱の原因となってしまうこともあるかもしれません。
最近ではasync/awaitの機能が充実し、もうPromiseオブジェクトを宣言するようなことはあまりしなくなり、ますますPromiseから手が遠のきやすい環境になっています。
しかし、async/awaitの本質、もっと言えばモダンな非同期処理は、Promise-basedといって全てPromiseに基づいています (setTimeout??知らない子ですね) 。そのため、ある程度学ぶと必ずPromiseの話題が登場して困ってしまうわけです。
Promise、避けては通れない道ですね。

0
0
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
0
0