[Promise] Promise の使用 / コールバック地獄の問題 async-await / マクロ キューとマイクロ キュー

目次

1. 知識の復習と復習 

1. 関数オブジェクトとインスタンス オブジェクト

2. コールバック機能(キー理解)

2.1 コールバック関数とは?

2.2 コールバック関数の分類

3.エラー

3.1 エラーの種類

3.2 エラー処理

3.3 エラーオブジェクト

2. Promise の理解と使用

1、promise 理解

1.1 プロミスとは?

1.2 Promise 状態の変更

1.3 プロミスの基本的な流れ

2. Promise の使用

2.1. 重要な文法

2.2. 基本的なコーディングプロセス

2.3 使用 1: 基本的なコーディング プロセスを約束する

2.4 使用 2: promise を使用してタイマーベースの非同期をカプセル化する

2.5 使用 3: promise を使用して ajax 非同期要求をカプセル化する

3.約束のAPI

3.1 Promise コンストラクター: new Promise (エグゼキューター) { }

3.2 Promise.prototype.then メソッド: Promise インスタンス. then(onFulfilled, onRejected)

3.3 Promise.prototype.catch メソッド: Promise インスタンス.catch (onRejected)

3.4 Promise.resolve(値) / Promise.reject(理由)

3.5 Promise.all(promiseArr)

3.6 Promise.race(promiseArr)

3.7 Promise のいくつかの重要な問題

4. Promise はコールバック地獄の問題を解決します

4.1 コールバック地獄

4.2 その後のチェーンコール

4.3 then の連鎖呼び出しは、コールバック地獄を解決します (最善の方法ではありません)

4.4 約束の連鎖を破る

4.5 誤侵入

4.6 async - await はコールバック地獄を解決します (究極の解決策)

5. メリットを約束する

3. マクロキューとマイクロキュー

1。概要

2. 関連するインタビューの質問

2.1 質問タイプ 1

2.2 質問タイプ 2

2.3 質問タイプ 3


1. 知識の復習と復習 

1. 関数オブジェクトとインスタンス オブジェクト

関数オブジェクト: 関数をオブジェクトとして使用する場合、単に関数オブジェクトと呼びます。

    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        Person.sex = 'boy';
        console.log(Person.sex);
        console.log(Person.name); // Person.name 默认是构造函数名,无法自己定义
    </script>

インスタンス オブジェクト: 新しいコンストラクタまたはクラスによって生成されたオブジェクト。これをインスタンス オブジェクトと呼びます。

 const p = new Person('zs', 20);
 console.log(p); // Person {name: "zs", age: 20}

2. コールバック機能(キー理解)

2.1 コールバック関数とは?

私たちが定義したものは、を呼び出さず、最終的に実行されました。

2.2 コールバック関数の分類

(1) 同期コールバック関数:

理解:メイン スレッドですぐに実行され、コールバック キューには配置されません

例: Promiseの配列トラバーサル関連のコールバック関数・エグゼキュータ関数。

(2) 非同期コールバック関数:

理解:すぐには実行されませんが、コールバック キューに入れられ、後で実行されます。

例: タイマー コールバック/ajax コールバック/成功と失敗のコールバックを約束します。

JS 実行シーケンス:

イベント ループ:

3.エラー

公式ドキュメント:エラー - JavaScript | MDN

3.1 エラーの種類

エラー すべての偽の親の種類
参照エラー 参照された変数が存在しません
TypeError 不正なデータ型
構文エラー 文法上の誤り

3.2 エラー処理

エラーをキャッチ: try{ } catch(){ }

MDN ドキュメント: try...catch - JavaScript | MDN

try {
  nonExistentFunction();
} catch (error) {
  console.error(error);
  // expected output: ReferenceError: nonExistentFunction is not defined
  // Note - error messages will vary depending on browser
}

エラーをスローする: エラーをスローする

3.3 エラーオブジェクト

message 属性: エラー関連情報。

stack 属性: レコード情報。

2. Promise の理解と使用

1、promise 理解

1.1 プロミスとは?

抽象表現:

(1) Promise は新しい技術 (ES6 仕様) です。

(2) Promise は、 JS での非同期プログラミング新しいソリューションです(古いソリューションは、単純にコールバック関数を使用することです)。

具体的な表現:

(1) 構文: Promise はコンストラクターです。

(2) 機能的な観点から: promise オブジェクトは、非同期操作をカプセル化するために使用され、その成功/失敗の結果値を取得できます。

理解:

(1) Promise はコールバックではなく、組み込みのコンストラクタであり、プログラマ自身の new によって呼び出されます。

(2) 新しい Promise を作成するときは、コールバック関数 (エグゼキュータ関数) を渡す必要があります。これは同期コールバックであり、メイン スレッドですぐに実行されます。

(3) エグゼキューター関数は2 つのパラメーターを受け取ります。どちらも関数であり、正式なパラメーターと共に受け取ります: それぞれ解決と拒否。

① resolve を呼び出すと、Promise インスタンスの状態が : finished に変更され、成功値を指定できます。

② reject を呼び出すと、Promise インスタンスの状態が : failed (rejected) に変更され、失敗の理由を指定できます。

注:コールバック関数のエグゼキューター自体は同期であり、エグゼキューター内の関数は非同期です。

1.2 Promise 状態の変更

各 Promise インスタンスには、初期化 (保留中)、成功 (実行済み)、失敗 (拒否) の 3 つの状態があります。

各 Promise インスタンスが新しくなった瞬間、状態は初期化 (保留中)になります。

Promise の状態は1 回しか変更できず、保留中 => 履行済み保留中 => 拒否済みの 2 種類の変更しかありません

1.3 プロミスの基本的な流れ

2. Promise の使用

2.1. 重要な文法

新しい Promise(executor) コンストラクター
Promise.prototype.then() メソッド

2.2. 基本的なコーディングプロセス

1. Promise インスタンス オブジェクト(保留状態) を作成し、executor 関数に渡します2.
executor で非同期タスク (タイマー、ajax リクエスト) を開始します         3.非同期
タスクの結果に応じて異なる処理を行います。
非同期タスクが成功した場合:
            resolve(value) を呼び出して Promise インスタンス オブジェクトの状態を充足 (fulfilled) にし、同時に成功値
        3.2 を指定します. 非同期タスクが失敗した場合:
            reject(reason) を呼び出して作成します失敗の理由を指定しながら、Promiseインスタンスオブジェクトの状態が拒否されました(拒否されました)
 

4. then メソッドを使用して、Promise インスタンスの成功および失敗のコールバック関数を指定し、成功の値と失敗の理由を取得します。
        注: then メソッドで指定された成功のコールバックと失敗のコールバックはすべて非同期コールバックです。

5. ステータスについての注意点:
    1. 3 つのステータス:
        pending: 未定 ----- 初期ステータス
        満たされた: 成功 ------ resolve() を呼び出した後の状態
        拒否された: 失敗した --- --- 呼び出した後の状態reject( ) 2.         保留中 => 履行         保留中 => 却下の
    2 つの状態が変化します     3. 状態は 1 回だけ変更できます!!     4. promise で複数の成功/失敗コールバック関数が指定されている場合、それらはすべて呼び出されますか? 会議!




2.3 使用 1: 基本的なコーディング プロセスを約束する

<script>
// 1) 创建 promise 对象(pending 状态), 指定执行器函数
const p = new Promise((resolve, reject) => {
// 2) 在执行器函数中启动异步任务
setTimeout(() => {
    const time = Date.now()
    // 3) 根据结果做不同处理
    if (time % 2 === 1) {
        // 3.1) 如果成功了, 调用 resolve(), 指定成功的 value, 变为 resolved 状态
        resolve('成功的值 '+ time) } else { 
        // 3.2) 如果失败了, 调用 reject(), 指定失败的 reason, 变为rejected 状态
        reject('失败的值' + time) }
    }, 2000)
})
// 4) 能 promise 指定成功或失败的回调函数来获取成功的 vlaue 或失败的 reason
p.then(
    value => { // 成功的回调函数 onResolved, 得到成功的 vlaue
        console.log('成功的 value: ', value)
    },
    reason => { // 失败的回调函数 onRejected, 得到失败的 reason
        console.log('失败的 reason: ', reason) 
    } 
)
</script>

2.4 使用 2: promise を使用してタイマーベースの非同期をカプセル化する

<script>
        function doDelay(time) {
            // 1. 创建 promise 对象
            return new Promise((resolve, reject) => {
                // 2. 启动异步任务
                console.log('启动异步任务')
                setTimeout(() => {
                    console.log('延迟任务开始执行...')
                    const time = Date.now() // 假设: 时间为奇数代表成功, 为偶数代表失败
                    if (time % 2 === 1) { // 成功了
                        // 3. 1. 如果成功了, 调用 resolve()并传入成功的 value
                        resolve('成功的数据 ' + time)
                    } else { // 失败了
                        // 3.2. 如果失败了, 调用 reject()并传入失败的 reason
                        reject('失败的数据 ' + time)
                    }
                }, time)
            })
        }
        const promise = doDelay(2000)
        promise.then(
            value => {
                console.log('成功的 value: ', value)
            },
            reason => {
                console.log('失败的 reason: ', reason)
            },
        )
    </script>

2.5 使用 3: promise を使用して ajax 非同期要求をカプセル化する

 <script>
        /*
        可复用的发 ajax 请求的函数: xhr + promise
        */
        function promiseAjax(url) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                xhr.onreadystatechange = () => {
                    if (xhr.readyState !== 4) return
                    const { status, response } = xhr
                    // 请求成功, 调用 resolve(value)
                    if (status >= 200 && status < 300) {
                        resolve(JSON.parse(response))
                    } else { // 请求失败, 调用 reject(reason)
                        reject(new Error('请求失败: status: ' + status))
                    }
                }
                xhr.open("GET", url)
                xhr.send()
            })
        }
        promiseAjax('https://api.apiopen.top2/getJoke?page=1&count=2&type=video').then(
            data => {
                console.log('显示成功数据', data)
            },
            error => {
                alert(error.message)
            })
    </script>

3.約束のAPI

3.1 Promise コンストラクター: new Promise (エグゼキューター) { }

Executor 関数: 同期的に実行されます (解決、拒否) => { }

resolve 関数: resolve を呼び出して、Promise インスタンスの内部状態を成功 (履行済み) に変更します。

reject 関数: reject を呼び出して、Promise インスタンスの内部状態を失敗 (拒否) に変更します。

説明: Executor 関数は Promise 内ですぐに同期的に呼び出され、非同期コードが Executor 関数に配置されます

3.2 Promise.prototype.then メソッド: Promise インスタンス. then(onFulfilled, onRejected)

onFulfilled : 成功したコールバック関数 - (値) => { }

onRejected : 失敗のコールバック関数 - (理由) => { }

: then メソッドは、新しい Promise インスタンス オブジェクトを返します。

3.3 Promise.prototype.catch メソッド: Promise インスタンス.catch (onRejected)

onRejected :失敗のコールバック関数- (理由) => { }

説明: catch メソッドは、 then(undefined, onRejected) と同等のthen メソッドのグラマティック シュガーです

reject 関数はエグゼキュータで実行されます。

最初の p.then メソッドのコールバック関数は、成功したコールバックのみを指定し、失敗したコールバックを指定していないため、エラーキャッチされない例外 ( Uncaught ... )が生成されます。

2 つ目の p.catch メソッドは、実際には p.then の文法的な糖衣です.デフォルトでは成功したコールバックを指定せず、失敗したコールバックのみを指定し、reject 関数の情報を出力します。

さらに、resolve 関数がエグゼキュータで実行され、成功したコールバックが以下で指定されていない場合、エラーは報告されません。

3.4 Promise.resolve(値) / Promise.reject(理由)

Promise.resolve(値):

説明: ステータスが満たされたまたは拒否されたPromise インスタンス オブジェクトをすばやく返すために使用されます

注: value の値は次のようになります: (1) 配列、文字列などの非 Promise 値(2) 約束値。

① reject が最初に呼び出された場合、失敗した Promise 値が返され、次に resolve が呼び出され、最後に失敗した Promise 値が返されます。

②resolveを先に呼ぶと成功したPromiseの値が返ってきてrejectが呼ばれて最後に失敗したPromiseのコールバック関数が実行されるが、reasonは前回の成功したPromiseの値

 Promise.reject(reason):ステータスを拒否する必要がある Promise インスタンス オブジェクトをすばやく返すために使用されます

 な         

    

// 测试:如果方法给的是 p0 参数,打印出各种可能的结果
    // 为了方便看,不进行代码的注释。
    <script>
        // resolve - resolve
        const p0 = Promise.resolve('ok');
        const p = Promise.resolve(p0); // succ:  ok
 
        // reject - reject
        const p0 = Promise.reject('no!');
        const p = Promise.reject(p0); //fail:  Promise {<rejected>: "no!"} + 报错:Uncaught (in promise) no!
 
        // resolve - reject
        const p0 = Promise.resolve('ok');
        const p = Promise.reject(p0); // fail:  Promise {<fulfilled>: "ok"}
 
        // reject - resolve
        const p0 = Promise.reject('no!');
        const p = Promise.resolve(p0); // fail:  no!
 
        p.then(
            (value) => { console.log('succ: ', value); },
            (reason) => { console.log('fail: ', reason); }
        )
    </script>

3.5  Promise.all(promiseArr)

promiseArr: n 個の Promise インスタンスを含む配列。

説明:すべての promise が成功した場合にのみ、新しい Promise インスタンスを返します。成功した値は、成功したすべての promise 値のセットです; 1 つが失敗する限り、それは直接失敗し失敗した Promise に遭遇すると、直接失敗した Promise 値を返します。背後にある Promise 値は気にしません。

    <script>
        const p1 = Promise.resolve('0');
        const p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                // resolve('500');
                reject('500');
            }, 500)
        });
        const p3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('2000');
            }, 2000)
        })
        const x = Promise.all([p1, p2, p3]);
        const then = x.then(
            (value) => { console.log('success', value); },
            (reason) => { console.log('fail', reason); }   // 'fail', '2000'
        )
    </script>

3.6  Promise.race(promiseArr)

promiseArr: n 個の Promise インスタンスを含む配列。

説明: 新しい Promise インスタンスを返し、最初の Promise インスタンスの値を返します。

3.7 Promise のいくつかの重要な問題

1. Promise インスタンスの状態を変更するには?

  • 解決の実行 (値): 現在保留中の場合は、履行されます。
  • 拒否の実行 (理由): 現在保留中の場合、拒否されます。
  • エグゼキューター関数 (エグゼキューター) が例外をスローします。現在保留中の場合、拒否されます。

Promise 状態は 1 回だけ変更でき、上記は成功を返し、その後の未定義の例外によって Promise 状態が変更されることはありません。

2. Promise インスタンスの状態を変更し、最初にコールバック関数を指定する必要があるのはどれですか?
        ①可能です通常はコールバックを指定してから状態を変更しますが、状態を変更してからコールバックを指定することも可能です


       ②状態を変えてからコールバックを指定する方法は?
                しばらく待ってから then( ) を呼ぶ


        ③Promiseインスタンスはいつデータを取得できるのか?
                最初にコールバックを指定すると、状態が変化したときにコールバック関数が呼び出されてデータを取得します。
                状態が最初に変更された場合、コールバックが指定されたときにコールバック関数が呼び出されてデータが取得されます。

3. Promise instance.then() は [新しい Promise インスタンス] を返しますが、その値と状態を決定するものは何ですか? 1. 
                簡単な式: then() で指定されたコールバック関数の実行結果が決定されます。
                2. 詳細式:
                        (1) then で指定されたコールバックがPromise 値 a:                                     then を返す場合、[新しい Promise インスタンス] ステータスは:成功 (履行済み)であり、成功値は a です。                         (2) then で指定されたコールバックがPromise インスタンス p:を返す場合                                     、[新しい Promise インスタンス] の状態と値は p と一致します                         (3) then で指定されたコールバックが例外をスローした場合:                                      [新しい Promise インスタンス] の状態は拒否され、理由はスローされた例外です。




      

 p.then 出力成功 1、a、戻り値は非 Promise 値である 900、

次に x.then を呼び出し、2,900 を正常に返します。

     

 p.then output failed 1、a、戻り値は未定義で、Promise 以外の値です。

次に x.then を呼び出し、未定義の 2 を正常に返します。

   

    

4. Promise はコールバック地獄の問題を解決します

4.1 コールバック地獄

コールバック地獄とは?

コールバック関数はネストされて呼び出され、外部コールバック関数の非同期実行の結果は、ネストされたコールバックの実行の条件になります。

例: 最初の要求が成功した後に 2 番目の要求を開始し、2 番目の要求の後に 3 番目の要求を開始する必要があるため、レイヤーごとのネスティングが発生します。

コールバック地獄の短所:プログラマにとって読みにくい、例外処理に不便、後のメンテナンスに不便。

4.2 その後のチェーンコール

Promise インスタンス. then( ) は、then() で指定されたコールバック関数の実行結果によって値と状態が決定される新しい Promise インスタンスを返します。

(1) then で指定されたコールバックが:新しい Promise インスタンスの状態は満たされ、成功値は a です。

    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        const x = p.then(
            (value) => { console.log('succ_then1:', value); }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); }
        )
 
        x.then(
            (value) => { console.log('succ_then2:', value); }, // succ_then2: undefined
            (reason) => { console.log('fail_then2:', reason); }
        )
    </script>
    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        const x = p.then(
            (value) => { console.log('succ_then1:', value); return false }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); }
        )
 
        x.then(
            (value) => { console.log('succ_then2:', value); }, // succ_then2: false
            (reason) => { console.log('fail_then2:', reason); }
        )
    </script>

(2) then で指定されたコールバックがPromise インスタンス p を返す場合:新しい Promise インスタンスの状態と値は p と一致します。

    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        const x = p.then(
            (value) => { console.log('succ_then1:', value); return Promise.resolve('a') }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); }
        )
 
        x.then(
            (value) => { console.log('succ_then2:', value); }, // succ_then2: a
            (reason) => { console.log('fail_then2:', reason); }
        )
    </script>
    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        const x = p.then(
            (value) => { console.log('succ_then1:', value); return Promise.reject('a') }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); }
        )
 
        x.then(
            (value) => { console.log('succ_then2:', value); },
            (reason) => { console.log('fail_then2:', reason); } // fail_then2: a
        )
    </script>

(3) then で指定されたコールバックが例外をスローした場合:新しい Promise インスタンスの状態は拒否され、理由はスローされた例外です。

    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        const x = p.then(
            (value) => { console.log('succ_then1:', value); throw 404 }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); }
        )
 
        x.then(
            (value) => { console.log('succ_then2:', value); }, 
            (reason) => { console.log('fail_then2:', reason); } // fail_then2: 404
        )
    </script>

包括的なケース:

    <script>
        const p = new Promise((resolve, reject) => {
            resolve('a');
        })
 
        p.then(
            (value) => { console.log('succ_then1:', value); throw 404 }, // succ_then1: a
            (reason) => { console.log('fail_then1:', reason); return 10 }
        ).then(
            (value) => { console.log('succ_then2:', value); return 100 },
            (reason) => { console.log('fail_then2:', reason); return Promise.reject('20') } // fail_then2: 404
        ).then(
            (value) => { console.log('succ_then3:', value); return true },
            (reason) => { console.log('fail_then3:', reason); return false } // fail_then3: 20
        ).then(
            (value) => { console.log('succ_then4:', value); },
            (reason) => { console.log('fail_then4:', reason); } // succ_then4: false
        )
    </script>

4.3 then の連鎖呼び出しは、コールバック地獄を解決します (最善の方法ではありません)

説明:最初のリクエストが成功したら、値 => { } の成功したコールバックを呼び出し、Promise インスタンス (2 番目のリクエストのインスタンス) を返します。前項の then() メソッドの説明によると、 then で指定されたコールバックが Promise インスタンス p を返す場合、新しい Promise インスタンスの状態と値は p と一致します。したがって、実際には、2 番目のリクエストの Promise インスタンスを最初のリクエストの戻り値として渡しています。

        promiseAjax(url)
            .then(
                value => {
                    console.log('显示第1次成功的数据', value);
                    // 返回第二次请求的实例
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); }
            )

完全なコード:

    <script>
        // 定义一个发送请求的函数,返回一个新的 Promise 实例(封装了 ajsx 异步任务)
        function promiseAjax(url) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.onreadystatechange = () => {
                    if (xhr.readyState !== 4) return;
                    const { status, response } = xhr;
                    if (status >= 200 && status < 300) {
                        // 请求成功, 调用 resolve(value)
                        resolve(JSON.parse(response));
                    } else {
                        // 请求失败, 调用 reject(reason)
                        reject(new Error('请求失败: status: ' + status));
                    }
                }
                xhr.open("GET", url);
                xhr.send();
            })
        }
        
        // 定义正确和错误的 url 地址便于之后的测试
        const url = 'https://api.apiopen.top/api/getHaoKanVideo?page=0&size=2';
        const url_error = 'https://api.apiopen.top/api22/getHaoKanVideo?page=0&size=2';
 
        // then 的链式调用解决回调地狱问题
        promiseAjax(url)
            .then(
                value => {
                    console.log('显示第1次成功的数据', value);
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); }
            )
            .then(
                value => {
                    console.log('显示第2次成功的数据', value);
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); }
            )
            .then(
                value => {
                    console.log('显示第3次成功的数据', value);
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); }
            )
            .then(
                value => {
                    console.log('显示第4次成功的数据', value);
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); }
            )
 
    </script>

4.4 約束の連鎖を破る

問題が発生します: 4.3 でコードを実行すると、2 番目の要求が失敗すると仮定して、ERROR エラーがスローされ、理由 => {} 失敗したコールバックが呼び出されますが、残念ながら、失敗したものに値を返しませんでした。 callback であるため、undefined を返します。undefined はたまたま非 Promise 値であり、後続のリクエストで成功につながります! これは明らかに間違っています。

問題解決:リクエストが失敗した後、リクエストは続行されません。

解決策:失敗したコールバックごとに保留中の Promise インスタンスを返します。

        promiseAjax(url)
            .then(
                value => {
                    console.log('显示第1次成功的数据', value);
                    return promiseAjax(url_error)
                },
                reason => { alert(reason.message); return new Promise(() => { }) }
            )
            .then(
                value => {
                    console.log('显示第2次成功的数据', value);
                    return promiseAjax(url)
                },
                reason => { alert(reason.message); return new Promise(() => { }) }
            )

4.5 誤侵入

問題:各 then() メソッドで失敗コールバックを指定する必要があり、Promise チェーンを中断するために値を返す必要があります. これらの失敗コールバック コードは非常に似ているため、面倒です.

解決策: .catch() メソッドを使用して最終的な結果をキャッチします。

基本的な考え方: .catch() メソッドを使用して、すべてのエラーの失敗コールバックを指定します. 実際の考え方は、 then() メソッドで失敗コールバックを指定していませんが、最下層がそれを補っているということです. 理由 => {throw reason }、then で指定されたコールバックが例外をスローした場合、新しい Promise インスタンスの状態は拒否され、reason はスローされた例外です。したがって、実際には、失敗のコールバックが .catch() メソッドに渡され続け、最終的に例外がスローされます。

        promiseAjax(url)
            .then(
                value => {
                    console.log('显示第1次成功的数据', value);
                    return promiseAjax(url_error)
                },
                // 我们虽然没有写失败的回调,但实际上底层为我们补了以下代码:
                // reason => {throw reason}
            )
            .then(
                value => {
                    console.log('显示第2次成功的数据', value);
                    return promiseAjax(url)
                },
                // 我们虽然没有写失败的回调,但实际上底层为我们补了以下代码:
                reason => {throw reason}
            )
            .catch(
                reason => { alert(reason.message); }
            )

4.6 async - await はコールバック地獄を解決します(究極の解決策)

文法の説明:

(1) 非同期変更関数:

①関数の戻り値はpromiseオブジェクトです。

② promise インスタンスの結果は、非同期関数実行の戻り値によって決まります。

(2) await式:

① 式が promise インスタンス オブジェクトの場合、await 後の戻り値は promise の成功値です。

②式が別の値なら、この値をそのままawaitの戻り値にする(代入演算と同等、ダメそう…)

(3) 注:

async 関数には await を書かなければならないが、async 関数には await を書かなくてもよい(単純な関数の前に async を書いても無駄に思える…)。

②await のpromise インスタンス オブジェクトが失敗した場合、例外がスローされます。これをキャプチャして、try...catch で処理する必要があります。

非同期待機構文:

// 函数形式写法
async function demo() {
    try {
        const result = await promiseAjax(url);
        console.log(result);
        console.log(100);
    } catch (error) {
        console.log(error);
    }
}
demo();
 
// 箭头函数形式写法
// 注意:如果前面没分号,需要在函数前面加分号或者感叹号,否则会报错。
(async () => {
    try {
        const result = await promiseAjax(url);
        console.log(result);
        console.log(100);
    } catch (error) {
        console.log(error);
    }
})()

await の原則:

await で async を使用する場合:
1. 表面にコールバック関数はありません。
2. しかし実際には、最下層は私たちが書いたコードを処理し、コールバック関数を「復元」しました。
3. 最終的に実行されるコードにはまだコールバックがありますが、プログラマーには表示されません。

        (async () => {
            try {
                // 程序员“实际上”的写法
                const result = await promiseAjax(url);
                console.log(result);
                console.log(100);
 
                // 浏览器翻译后的代码(表面上没有调then,实际上调了then)
                promiseAjax(url).then(
                     (value) => {
                         console.log(value);
                         console.log(100);
                     }
                 )
            } catch (error) {
                console.log(error);
            }
        })()
        console.log('主线程');
        // 输出:先执行主线程,函数瞬间调用完,将异步函数推进队列,等待调用执行。
        //      主线程
        //      {code: 200, message: "成功!", result: {…}}
        //      100

async / await リクエストが成功しました 

失敗した

5. メリットを約束する

利点:
1. コールバック関数を指定する方法はより柔軟です:
        古い: 非同期タスクを開始する前に promise を指定する必要があります
        : 非同期タスクを開始する => promise オブジェクトを返す => コー​​ルバック関数を promise オブジェクトにバインドします (非同期タスクの終了後に指定することもできます )

2. コールバック地獄の問題を解決できる連鎖呼び出しをサポート
        (1) コールバック地獄とは:
                コールバック関数のネストされた呼び出し、外部コールバック関数の非同期実行の結果は、ネストされたコールバック関数の実行。

        (2)コールバック地獄のデメリット:
                コードが読みにくい、例外処理がしにくい。

        (3) あまり良くない解決策:
                then のチェーン呼び出し
        (4) 究極の解決策:
                async/await (最下層は実際にはまだ then のチェーン呼び出しを使用しています)

3. マクロキューとマイクロキュー

1。概要

JS で実行されるコールバック関数を格納するために使用されるキューには、2 つの異なる特定のキューが含まれています:
        マクロ キュー:タイマー コールバック、DOM イベント コールバック、および ajax コールバックなど、実行されるマクロ タスク (コールバック) を保存するために使用されます。

        マイクロキュー:実行するマイクロタスク (コールバック) を保存するために使用されます。たとえば、promise コールバック、 MutationObserver コールバックなどです。現在接触している唯一のマイクロタスクは Promise コールバックです。

        実行: JS エンジンは、最初にすべての初期化同期タスク コードを実行する必要があります.最初のマクロ タスクの実行準備が整う前に、すべてのマイクロ タスクを取り出して 1 つずつ実行する必要があります.つまり、マイクロ タスクの優先度が高くなります.マクロタスクよりも、マイクロタスクが実行された後にマクロタスクが実行され、マイクロタスクのコード位置とは関係ありません。

リファレンス: JS 非同期マクロ キューとマイクロ キュー - BAHG - 博客园

2. 関連するインタビューの質問

マクロ キュー: [マクロ タスク 1, マクロ タスク 2...]
マイクロ キュー: [マイクロ タスク 1. マイクロ タスク...]
ルール: マクロ キュー内のタスクを実行する前に、まずマイクロ 保留中のマイクロ タスクがあるかどうかキューで。
        1.存在する場合は、最初にマイクロタスクを実行します。
        2.そうでない場合は、マクロ キュー内のタスクの順序に従って順番に実行します。

2.1 質問タイプ 1

マイクロタスクは、マクロタスクの前に実行されます。

        setTimeout(() => {
            console.log('timeout1');
        })
        setTimeout(() => {
            console.log('timeout2');
        });
        Promise.resolve(3).then(
            value => { console.log('成功了1'); }
        )
        Promise.resolve(4).then(
            value => { console.log('成功了2'); }
        )

2.2 質問タイプ 2

マイクロタスクはマクロタスクで呼び出されます。

マクロ タスク キュー: 3 秒後にtimeout2 ; 5 秒後にtimeout1 、 次にtimeout3 をマクロ タスクにプッシュし次に成功 5 をマイクロ タスクにプッシュします

マイクロタスク キュー:成功 3; 成功 4; 成功 5

実行結果:マイクロタスクが最初に実行された:成功 3; 成功 4;マクロタスクの実行: 3 秒間隔の timeout2が最初に実行され、 5 秒間隔の timeout1が再度実行され、次にtimeout3がマクロタスクにプッシュされ成功5マイクロタスク task にプッシュされます最初にマイクロタスクが正常に実行され 5 、次にマクロタスクのタイムアウト 3 が実行されます。

注: マクロタスク内のマイクロキューに配置されたマイクロタスクが存在する場合があり、この時点でマイクロタスクが最初に実行されます。

        setTimeout(() => {
            console.log('timeout1');
            setTimeout(() => {
                console.log('timeout3');
            })
            Promise.resolve(5).then(
                value => { console.log('成功了5'); }
            )
        }, 5000)
        setTimeout(() => {
            console.log('timeout2');
        }, 3000)
        Promise.resolve(3).then(
            value => { console.log('成功了3'); }
        )
        Promise.resolve(4).then(
            value => { console.log('成功了4'); }
        )

2.3 質問タイプ 3

    <script>
        setTimeout(() => {
            console.log('0');
        });
        new Promise((resolve, reject) => {
            console.log('1');
            resolve();
        }).then(() => {
            console.log('2');
            new Promise((resolve, reject) => {
                console.log('3');
                resolve();
            }).then(() => {
                console.log('4');
            }).then(() => {
                console.log('5');
            })
        }).then(() => {
            console.log('6');
        })
        new Promise((resolve, reject) => {
            console.log('7');
            resolve();
        }).then(() => {
            console.log('8');
        })
    </script>

 

 

出力: 1 7 2 3 8 4 6 5 0

おすすめ

転載: blog.csdn.net/qq_37308779/article/details/126130378