16
2017

Promiseのオブジェクトを判定する方法

CATEGORYJavaScript
TypeScriptでちょっとしたライブラリを作ってたのだけど、いざそれをJavaScriptのアプリに組み込んで使ったところ、Promiseがうまく判定できずトラぶったのでメモ。

今回やりたかった事はこんな感じの、Promiseだったらそのまま、PromiseじゃなかったらPromise.resolve()でラップして返すみたいな、callback的な処理で戻り値の型を統一するような奴。
(実際のコードはcatchを付けてたりともっと複雑。このサンプルだと端折り過ぎで実害はないかも?イメージという事で。)
const result = handler(params);
if (result instanceof Promise) {
return result;
}
return Promise.resolve(result);
ごく普通の発想だとこんな感じになると思われる。ユニットテスト書いてライブラリ単体で動かしている分には特に問題なく動いていたのだが、これを多数のライブラリを組み込んだ複雑なアプリから呼び出したところで問題発覚。
戻り値にPromiseを返しているのに、明らかにif文に入っていない挙動を示した。

しばらく考えた後にどうも先日と同様のbluebird絡みの問題であるっぽいことに気づく。呼び出し元のアプリでは、諸般の事情からOSSのPromiseライブラリであるbluebirdも参照しており、どうも標準のPromiseとbluebirdのPromiseを比較して、違うクラスと判定しているようであった。

…という事でどうしたもんかとググったところ、stackoverflowで正に同じ悩みを発見。
結論から言うと「Promiseの規格では then メソッドが必ず存在するため、then の有無をチェック」すれば、標準やbluebirdのPromise、それにQといった他のPromise実装も含めて判別できるということ。なるほど。

それに基づいて修正したコードはこんな感じ。
function isPromise(obj: any): boolean {
return obj instanceof Promise || (obj && typeof obj.then === 'function');
}

const result = handler(params);
if (isPromise(result)) {
return result;
}
return Promise.resolve(result);
ただ、これだともちろん then というメソッドがあるだけの全然関係ないインスタンスもPromiseと判定されてしまうので、その辺はトレードオフ。
stackoverflowでは他にも「単にPromiseに変換したいだけなら、害はないので問答無用で Promise.resolve(obj) してしまえ」とった回答もされているので、自分の用途に合わせた方法を用いればよさそう。
スポンサーサイト

Tag: JavaScript TypeScript

0 Comments

Leave a comment