JS 非同期コードユニットテストの魔法 Promise

JS 非同期コードユニットテストの魔法 Promise

序文

この記事を書いた理由は、ユニット テストを書くときに、次のテストを実行したからです。

新しい Promise((resolve, deny) => deny(1)).then().catch(err => {
    コンソール.log(エラー)
})
非同期関数jestTest(){
    Promise.resolve().then() を待つ
    console.log('この時点で、catch が呼び出され、ログが出力されていると予想されます')
}
jestテスト()

イベント ループで catch が呼び出されるまで await を使用してテスト コードをブロックすることはできません。これにより、catch の実行が検出され、テストが合格します。

「魔法」という言葉が使われているのは、promsie のチェーン呼び出しには確かに多くのデフォルト ハンドラーと暗黙的な値の転送があるためです。

プロミスチェーン

時間を無駄にしないために、例を見てみましょう。

Promise.resolve('promise1')
.then(res => {
    console.log('promise1-1 then')
})
.then(res => {
    console.log('promise1-2 then')
})
.then(res => {
    console.log('promise1-3 then')
})
.then(res => {
    console.log('promise1-4 then')
})


Promise.resolve('promise2')
.then(res => {
    console.log('promise2-1 then')
    新しいエラーをスローします('モックエラー1')
})
.then(res => {
    console.log('promise2-2 then')
    新しいエラーをスローします('モックエラー2')
})
.catch(エラー => {
    コンソール.log(エラー)
})

上記のコード出力シーケンスに対する回答が次のものと同じ場合は、この記事をスキップできます。

約束1-1

約束2-1

約束1-2

約束1-3

エラー: モックエラー 1

約束1-4

まず前提として、これら 2 つのプロミスの then 呼び出しが交差して積み重ねられていることが既にわかっている必要があります (出力の最初の 3 行からわかるように)。この部分についてよくわからない場合は、イベント ループに関する関連記事を参照してください。同時に、記事で指定されているバージョンでは、Chrome と Nodejs のイベント ループ メカニズムはすでに同じであることに注意してください。

MDN エラー

catch の元の(私が変更した)MDN の説明を見てみましょう。

基本的に、例外が発生すると Promise チェーンは停止し、代わりにチェーン内で catch ハンドラーを探します。

例外が発生するとチェーン呼び出しは停止し、チェーン内で catch ステートメントが見つかり、実行されます。

私の最初の誤解も同じでした。catch は最初にスローされた Error を直接キャッチする、つまり、Error は promise1-2 の後に出力され、promise2-2 が配置されている then はコール スタックに追加されないと誤解していました。

しかし、実際の出力結果を観察すると、そうではないことがわかります。これは、MDN の説明の文字通りの意味が間違っていることを示しています。チェーン呼び出しは停止せず、私たちが見ていない何かを実行しました。

連鎖デフォルト処理

この時点では、デフォルトの処理を知る必要があり、MDN の説明も直接引用します。

then が呼び出される Promise が、then にハンドラーがない状態 (履行または拒否) を採用する場合、then が呼び出された元の Promise の最終状態を単純に採用して、追加のハンドラーなしで新しい Promise が作成されます。

プロミスの then に対応する状態処理のコールバックがない場合、then はこのプロミスの状態を受け入れるプロミスを自動的に生成します。つまり、then は同じ状態参照を持つプロミスを返し、それを後続の呼び出しに渡します。

上記のコードの2番目のpromise部分は次のようになります。

Promise.resolve('promise2')
.then(res => {
    console.log('promise2-1 then')
    新しいエラーをスローします('モックエラー1')
})
.then(res => {
    console.log('promise2-2 then')
    新しいエラーをスローします('モックエラー2')
// onRejected に注意してください
}, (エラー) => {
    Promise.reject(err) を返します。
})
.catch(エラー => {
    コンソール.log(エラー)
})

つまり、出力結果の promise1-2 と promise1-3 の間で promise2-2 の then が実行されるため、チェーン呼び出しは直接停止されず、promise2-2 の then が呼び出しスタックに追加されたままになります。キャッチは、直接キャッチされる最初の then によってスローされたエラーではなく、非表示の onRejected によって返される同じステータスの約束です。

略語

同様に、catch(onRejected) は then(undefined, onRejected) の省略形であることを知っておく必要があります。つまり、呼び出しチェーンの前の呼び出しでエラーがなくても、catch は呼び出しスタックを直接スキップするのではなく、呼び出しスタックに入ります。

Promise.resolve('promise1')
.then(res => {
    console.log('promise1-1 then')
})
.then(res => {
    console.log('promise1-2 then')
})
.then(res => {
    console.log('promise1-3 then')
})


Promise.resolve('promise2')
.then(res => {
    console.log('promise2-1 then')
})
.catch(エラー => {
    コンソール.log(エラー)
})
.then(res => {
    console.log('実際はpromise2-3です')
})

非同期待機

最初に注意すべきことは、この記事で指定されている NodeJs および Chrome バージョンでは、f(await promise) は promise.then(f) と完全に同等であるということです。

もちろん、Promise について議論するときに、async await を無視することはできません。プロミスの状態が onResolve のときは両者の処理ロジックは同じですが、エラー処理の実行ロジックが異なります。async await でエラーが発生すると、後続の await の実行が本当に直接スキップされます。

const promiseReject = new Promise((resolve, deny) => {
    拒否(新しいエラー('error'))
})
const promiseResolve1 = 新しい Promise((resolve, 拒否) => {
    解決('正しい')
})
const promiseResolve2 = 新しい Promise((resolve, 拒否) => {
    解決('正しい')
})
const promiseResolve3 = 新しい Promise((resolve, 拒否) => {
    解決('正しい')
})
関数demo1() {
    約束拒否
    .then(() => {
        コンソールログ('1-1')
    })
    .catch(エラー => {
        コンソールログ('1-2')
    })
}

非同期関数demo2(){
    試す {
        待つ約束拒否
        約束を待つ解決1
        promiseResolve2を待つ
        約束を待つResolve3
    } キャッチ(エラー){
        コンソールログ('2-1')
    }
}
// 2-1
// 1-2

上記は、JS 非同期コード単体テストの魔法の約束の詳細です。JS 非同期コードの約束の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • js Promise同時制御メソッド
  • JavaScriptはPromiseを使用して複数の繰り返しリクエストを処理します
  • JavaScript で Promise を使用して同時リクエスト数を制御する方法
  • JS 9 Promise 面接の質問
  • JS の Promise に中止関数を追加する方法
  • JS 非同期スタック トレース: await が Promise よりも優れている理由
  • Javascript 非同期プログラミング: Promise を本当に理解していますか?
  • JavaScript PromiseとAsync/Awaitの詳細な説明
  • フロントエンドJavaScriptの約束

<<:  MySQL に絵文字を保存するときに表示されるエラー メッセージ「java.sql.SQLException: 文字列値が正しくありません:'\xF0\x9F\x92\xA9\x0D\x0A...'」の解決方法

>>:  VMware で Centos7 ブリッジ ネットワークを構成する手順の詳細な説明

推薦する

MySQL に絵文字表現を挿入できない問題の解決方法

序文最近この問題に遭遇するまで、私は UTF-8 が文字セットの問題に対する普遍的な解決策だと考えて...

MySQLサーバのスレッド数を表示する方法の詳細な説明

この記事では、例を使用して、MySQL サーバーのスレッド数を表示する方法について説明します。ご参考...

Vue の一般的な問題と解決策の概要 (推奨)

Vue に限定されず、他の種類の SPA プロジェクトにも当てはまる問題がいくつかあります。 1....

Reactは複雑な検索フォームの展開と折りたたみ機能を実装します

時間に余裕を持って、過去を忘れましょう。前のセクションでは、[検索] フォームとクエリおよびリセット...

ウェブデザインにおけるインタラクション: ページングの問題に関する簡単な説明

機能: 前のページまたは次のページにジャンプします。要素: ページングの基本要素は、前のページ + ...

JavaScript関数の詳細な説明これを指す問題

目次1.関数内のこの方向1. 通常の機能2. コンストラクター3. オブジェクトメソッド4. イベン...

JavaScript JSON.stringify() の使用法の概要

目次1. 使用方法1. 基本的な使い方2. 2番目のパラメータ - フィルター3. 3番目のパラメー...

Linux システムでの nginx サーバーのインストールと負荷分散構成の詳細な説明

nginx (エンジン x) は、高性能な HTTP およびリバース プロキシ サーバー、メール プ...

Linux ダイナミックライブラリの生成と使用ガイドの詳細な説明

Linux での動的ライブラリ ファイルのファイル名は libxxx.so のようになります。ここで...

Linux CentOS MySQL データベースのインストールと設定のチュートリアル

MySQLデータベースのインストールに関するメモ、みんなで共有a) MySQL ソースインストールパ...

Nginx リバース プロキシはポート 80 のリクエストを 8080 に転送します

まず、一連の概念を理解しましょう。nginx リバース プロキシとは何でしょうか?リバース プロキシ...

CSSセレクターでの正規表現の使用

はい、CSS にも正規表現があります (アーメン) CSS で目立つための 2 つの強力なツール: ...

mysql-8.0.19-winx64 をインストールしてログインするための初心者向けチュートリアル (初心者必読)

目次1. インストールパッケージ(64ビット)をダウンロードする2. MySQLデータベースをインス...

MySQL SQL文の特殊処理文のまとめ(必読)

1.テーブル全体を更新します。データ行の列の値が空の場合は、別の列フィールドの値と同じにします。 ...

CSSポジショニングによる階層関係の問題の詳細な説明

絶対、相対、固定位置の位置決めabsolue: 絶対配置。上、下、左、右を使用して、配置先の親要素に...