noteのタイトル画像

約束(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もポチっとしていただけると、さらに喜びます。




ソフトウェア・エンジニアを40年以上やってます。 「Botを作りたいけど敷居が高い」と思われている方にも「わかる」「できる」を感じてもらえるように頑張ります。 よろしくお願い致します。