見出し画像

Promiseに関する最終論考

はじめに

非同期処理、書いてますか?

むずかしいですよね。

フロントエンドで発生する不具合の多くを、この非同期処理で占めているのではないでしょうか?

この記事は、非同期処理に関するフロントエンドの記法に関して、私が開発をする中で達した結論について、アウトプットしておこうという試みです。

間違っている個所がありましたら、ご連絡いただけますと嬉しいです!

結論

結論は2つ!

1)Promise.then, .catch, .finally は利用しない!
2)コールバック関数はPromiseベースに書き換える!

です!

非同期処理はお好きですか?

はい。私は大好きです。
もし非同期処理が無かったら、通信の結果をまって、(Loading…などの表示をしないで)画面の表示をしたり、ファイルの読み込みをしなければなりません。

また別スレッドを立てて処理を逃がすなど、複雑な制御が必要になります。この場合、スレッド間で共有する資源へのアクセスを厳密に制限する必要が出てきます。

Javascriptの非同期処理は、シングルスレッドによる安全性と、他システム連携による処理待ちに対する、大変優れた回答です。

Promise.then, .catch, .finally は利用しない!

次のソースを見てみましょう。

example.comへのアクセス

fetchで「https://example.com」にアクセスし、consoleに出力する。という内容です。

async/awaitを利用していて、エラー処理は書かれていません。

どのように記述したらよいでしょうか。

async/awaitを採用した場合、下記の記法が尤も適切でしょう。

done.

try, catch, finall句を利用して、自然に記述します。
Promise内のUncaughtErrorもtry句でcatchして処理できます。
(意外と知らない方が多いですね。)

では、async/awaitを記述しないで、これを記述します。このようになるでしょう。

うへえ…

ううーん。直感的に見ても、これは読みにくい。

fetch()とtext()が2か所Promiseしているので、.thenのなかでうまく記述してやらないと、引数に何が入ってくるか、怪しいです。バグ埋め込みやすいです。

また、失敗した場合の処理と成功した場合の処理を分けて記述しているため、流れがつかみにくい。

then, catchの流れについても、この順番で書かないと、エラーになります。

順番も大事。

catchはPromise<void>を返す関数です。

で、問題は、Promise.catchとtry-catch句を混ぜて使っているソースコードです! こいつは危ない!

async/awaitが利用できる環境であれば、try句で統一して実装するのがいいでしょう。

さあ、.catchで検索してサバンナを焼き払うのだ!(っていうのは過言ですが・・・)

コールバック関数はPromiseベースに書き換える!

ボタンをクリックしたら、テキストとしてファイルを読み込むという処理を考えてみましょう。
Promiseを利用しない場合、このように記述すると思います。

onclickのネスト深すぎ!

loadFileという共通処理を書いておき、ボタンがクリックされたときの処理をcallbackで仕込んでいます。ファイル読み込みはよく使う処理ですからね!

fileの読み込み後の処理はテキスト処理とは限らないので、個別の共通処理readFileとしています。

これめちゃくちゃ見にくいですよね。loadFile>readFile>無名関数>出力
って目がちかちかする。

callbackの内容が複雑になると、さらにソースが混乱に至ります。

では、このloadFile, readFile関数をcallbackではなく、Promiseで書き直しましょう。

手続き型記述最高!

かなり見やすくなりましたね。

「ファイルを取得する、ファイルを読む、出力」という順序が、そのまま同じスコープ内で完結しています。

loadFile, readFileは共通処理なので、ある程度複雑でも間違いは起きにくい。単体テストも個別に可能。しかしonclick処理は個別動作なので、できるだけ安全に見やすく書きたい。という点もポイントでしょうか。

試してみましょう

asyncを採用したファイル処理


callbackを利用したファイル処理


throwされたエラーの処理

//F12のdevToolから、コンソールに打ち込んでみましょう。
(async () => {
  try{
    await new Promise((res, rej) => {
        throw new Error('このエラーはどこへ行く??')
    })
  }catch(e){
    //catchできていれば、エラーは出ず、標準のコンソールに出力される。
    console.log(e);
  }
})();


kintoneをご利用の方へ

cybozu developer networkにはPromise.thenを利用したサンプルが多いです。
こちらは(おそらくですが)、IE11に対応するためか、後方互換性のためではないかと考えています。

参考記事
https://developer.cybozu.io/hc/ja/articles/202331474

kintone.api関数では第4引数にcallback(args : any) : voidを利用したサンプルがありますが、第3引数までにとどめると、戻り値はPromise<any>となります。ぜひお試しください。

kintoneカスタマイズの初学者のソースコードでは、第4引数に関する不具合がとても多い印象です。


この記事は以上です。お読みいただきありがとうございました。

この記事が気に入ったらサポートをしてみませんか?