
約束(Promise)の地(値)で見つけた、幸せの「青い鳥(bluebird)」
目次
・Promiseのタイムアウト処理に挑戦
・TimeoutErrorクラス作成・・・そしてハマる
・windowsよ、お前はなぜ。
・青い鳥「bluebird」に出会う
●Promiseのタイムアウト処理に挑戦
先日、noteにて報告させていただいた「Promise」関連の続編です。
Promise.raceを使用した「タイムアウト検出」処理なんかは役に立つ
と考えて、早速トライして、、、ハマりました(笑)
Promiseはresolveとrejectのどちらかを必ず呼び出さなければなりません。それ以外の状態を取れないからです。
成功した時にresolveを呼び出すのはまったく問題ありません。
処理が完了する前にタイムアウトした時にrejectを呼び出すのも「とりあえず」OKでしょう。
先日紹介した「Promiseの本」でも「例外をthrowするよりもrejectしなさい」と書かれていました。
しかし、
return Promise.reject(new Error());
とすると、エラーをcatchした時に
・タイムアウトして処理を抜けたのか
・別の理由でプログラムが例外を吐いたのか
の区別がつきません。
そこで「Errorクラスを拡張して”TimeoutError”というクラスを作って、そのクラスをcatchした時はタイムアウトだと判定すれば良い」じゃーん、って軽く思ったわけです。
●TimeoutErrorクラス作成・・・そしてハマる
クラスは至極簡単
class TimeoutError extends Error {}
です。
catchさえ出来ればいいので、もう本当に拡張だけで済ませました。
ちょうど仕事でも使う予定の処理があったので、一石二鳥という感じで処理を書きました。(会社の環境はwindows7です)
TimeoutErrorクラスのインスタンスを作り、①と②でErrorとTimeoutErrorのインスタンスとして認識されるかを確認することにしました。
const e = new TimeoutError();
if(e instanceof Error) console.log('it is Error'); // ①
if(e instanceof TimeoutError) console.log('it is TimeoutError'); // ②
結果は①は当然のことながらOK。
しかし、②がNGです。「it is TimeoutError」が表示されません。
さまざまな文献を調べること小一時間。
TimeoutError.prototype = Error.prototype;
の処理を追加してやっとTimeoutErrorのインスタンスであると認識してくれました。(後の調査で、これだけでは完全でないと分かるのですが、当面はこれでOK)
うーん。C++やC#,Javaの感覚で実装するとダメってことですね。classなんてものが実装されたのでクラスベースのオブジェクト指向言語だと勘違いして実装していましたが、JavaScriptは「プロトタイプベース」のオブジェクト指向言語です。
この辺りの取り扱いがちょっと違うのですね。
タイプを調べてみましょう。
console.log(Object.prototype.toString.call(e));
// → [object Error]
Errorだと認識しているようです。
コンストラクタで比較してみましょう。
console.log(e.constructor === TimeoutError);
// → false
あれれ・・・
型が異なると怒られてしまいました。
TimeoutErrorクラスからnewしているにも関わらず。
これもあちこちの文献を探して
TimeoutError.prototype.constructor = TimeoutError;
を追加することで、結果をtrueにすることができました。
結局最終的には
class TimeoutError extends Error {}
TimeoutError.prototype = Error.prototype; // これがないとinstanceof で判定がfalseになってしまう
TimeoutError.prototype.constructor = TimeoutError; // これがないと e.constructorでfalseになってしまう。
で落ち着きました。
しかし、とっても嫌らしい構文ですね。
●windowsよ、お前はなぜ。
これで解決かと思われましたが「そうは問屋が卸してくれない」でした。
自宅に帰ってMacOSX上でプログラムを動作させようと思って、間違えて「素のError拡張」部分だけで動作させたところ
class TimeoutError extends Error {} だけでも動くじゃん・・・
というオチがつきました。
node,npmのバージョンはmacもwindowsも同じはずなんだけど、、、。もう、なんやねんっていう境地です。
あれこれ試行錯誤しましたが、理由は分からず。
相当に嫌気がさしてきたころ、私は幸せの「青い鳥」を見つけることができたのです。
●青い鳥「bluebird」に出会う
angluarJS等を使っていた人なら「Q」というPromiseライブラリをご存知の方も多いと思います。
このQと並び称される高機能Promiseが「bluebird」(青い鳥)なのです。
このbluebirdは本家のPromiseには無い
・Promiseのキャンセル(Promiseの仕様にはキャンセルの仕様は無い)
・timeoutやdelayなどの拡張機能
を有していました。
いままで使ってきたsleep関数
export const sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('timeout: ' + time + '(msec)');
}, time);
});
};
などは、
export const sleep = (time) => {
return Promise.resolve().delay(time);
};
で済んでしまいました。
TimeoutErrorクラスもbluebirdに用意されていたので、
import {Promise, TimeoutError} from 'bluebird';
とインポートしてあげるだけで良かったのです。
結構遠回りしてきましたが、最終的には”努力すれば青い鳥は見つかる”と自分に言い聞かせて、これからも精進していきます。
ご期待いただければ「スキ」ボタンをポチっと、「フォロー」ボタンをクリック、よろしくお願いいたします。
note: https://note.mu/o_matsuo
twitter: @o_matsuo
もフォローしてくださると、喜びます。
あ、それから私の師匠である
コンドウ様のnoteもポチっとしていただけると、さらに喜びます。
いいなと思ったら応援しよう!
