await、async、イベントループ(マクロタスク、マイクロタスクキューの実行シーケンス)

1 非同期、待機

2 ブラウザのプロセスとスレッド

3 マクロタスクとマイクロタスクキュー

4 プロミス面接の質問分析

5 投げる、トライする、キャッチする、最後に

非同期関数 - 非同期関数の書き方

 // 普通函数
    // function foo() {}
    // const bar = function() {}
    // const baz = () => {}

    // 生成器函数
    // function* foo() {}

    // 异步函数
    async function foo() {
      console.log("foo function1")
      console.log("foo function2")
      console.log("foo function3")
    }
    foo()

    // const bar = async function() {}
    // const baz = async () => {}
    // class Person {
    //   async running() {}
    // }

非同期関数 - 非同期関数の戻り値

// 返回值的区别
    // 1.普通函数
    // function foo1() {
    //   return 123
    // }
    // foo1()

    // 2.异步函数
    async function foo2() {
      // 1.返回一个普通的值
      // -> Promise.resolve(321)
      return ["abc", "cba", "nba"]

      // 2.返回一个Promise
      // return new Promise((resolve, reject) => {
      //   setTimeout(() => {
      //     resolve("aaa")
      //   }, 3000)
      // })

      // 3.返回一个thenable对象
      // return {
      //   then: function(resolve, reject) {
      //     resolve("bbb")
      //   }
      // }
    }

    foo2().then(res => {
      console.log("res:", res)
    })

非同期関数 - 非同期関数の例外

非同期のコードにエラーや例外があった場合、後続のコードには影響しませんが、結果は Promise の拒否として渡されます。

 // "abc".filter()

    // 什么情况下异步函数的结果是rejected

    // 如果异步函数中有抛出异常(产生了错误), 这个异常不会被立即浏览器处理
    // 进行如下处理: Promise.reject(error)
    async function foo() {
      console.log("---------1")
      console.log("---------2")
      // "abc".filter()
      throw new Error("coderwhy async function error")
      console.log("---------3")

      // return new Promise((resolve, reject) => {
      //   reject("err rejected")
      // })

      return 123
    }

    // promise -> pending -> fulfilled/rejected
    foo().then(res => {
      console.log("res:", res)
    }).catch(err => {
      console.log("coderwhy err:", err)
      console.log("继续执行其他的逻辑代码")
    })

非同期関数 await キーワードの使用法

await は普通の変数や関数で単純に使用するだけでは意味がありません。promise と組み合わせて使用​​します。その機能は yield に似ています。promise が返るまで待ってから結果を受け取り、その後 await の次の行を実行します。 . (コードの実行が中断されます)。

// 1.普通函数
    // function foo1() {
    //   await 123
    // }
    // foo1()


    // 2.await关键字
    // await条件: 必须在异步函数中使用
    function bar() {
      console.log("bar function")
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(123)
        }, 100000)
      })
    }

    async function foo() {
      console.log("-------")
      // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
      const res1 = await bar()
      console.log("await后面的代码:", res1)
      const res2 = await bar()
      console.log("await后面的代码:", res2)

      console.log("+++++++")
    }

    foo()

非同期 function-await は非同期リクエストを処理します

Promise が Reject を返した場合、catch を使用して例外とエラーをキャプチャできます。または、getdata に try catch を書き込みます。

 function requestData(url) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(url)
          // reject("error message")
        }, 2000);
      })
    }

    async function getData() {
      const res1 = await requestData("why")
      console.log("res1:", res1)

      const res2 = await requestData(res1 + "kobe")
      console.log("res2:", res2)
    }

    getData().catch(err => {
      console.log("err:", err)
    })

非同期関数 - await と async の組み合わせ

通常の関数は Promise を返しますが、非同期 async キーワードを使用して作成された関数は呼び出し時に await を使用することもできます。

  // 1.定义一些其他的异步函数
    function requestData(url) {
      console.log("request data")
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(url)
        }, 3000)
      })
    }

    async function test() {
      console.log("test function")
      return "test"
    }

    async function bar() {
      console.log("bar function")

      return new Promise((resolve) => {
        setTimeout(() => {
          resolve("bar")
        }, 2000);
      })
    }

    async function demo() {
      console.log("demo function")
      return {
        then: function(resolve) {
          resolve("demo")
        }
      }
    }


    // 2.调用的入口async函数
    async function foo() {
      console.log("foo function")

      const res1 = await requestData("why")
      console.log("res1:", res1)

      const res2 = await test()
      console.log("res2:", res2)

      const res3 = await bar()
      console.log("res3:", res3)

      const res4 = await demo()
      console.log("res4:", res4)
    }

    foo()

シングルスレッド - コードシーケンスの実行方法

ブラウザで開いた各ページはプロセスとなり、プロセス内には多数のスレッドが存在しますが、JavaScript を実行するスレッドは 1 つだけです。

 let name = "why"
    name = "kobe"

    function bar() {
      console.log("bar function")
    }

    function foo() {
      console.log("foo function")
      // 1.在JavaScript内部执行
      // let total = 0
      // for (let i = 0; i < 1000000; i++) {
      //   total += i
      // }

      // 2.创建一个定时器
      setTimeout(() => {
        console.log("setTimeout")
      }, 10000);

      bar()
    }

    foo()

シングルスレッド - 非同期コードの実行方法

タイマーで設定された時間に関係なく、タイマー後のコードが最初に実行されます。

 イベントキュー、イベントループ。

タイマー、DOM モニター、ネットワーク リクエストはすべてイベント キューに追加され、イベント キューは先入れ先出しの順序で実行コンテキストにイベントを入力します。

 

<body>

  <button>按钮</button>
  
  <script>

    const btn = document.querySelector("button")
    btn.onclick = function() {
      console.log("btn click event")
    }

    console.log("Hello World")
    let message = "aaaa"
    message = "bbbb"

    setTimeout(() => {
      console.log("10s后的setTimeout")
    }, 0);

    console.log("Hello JavaScript")
    console.log("代码继续执行~~~")
    console.log("-------------")

  </script>

</body>

シングルスレッド - マイクロタスクとマクロタスクの違い

Promise 内の関数は外部に記述された関数と同じで、即時に実行され、イベント キューには配置されません。ただし、この Promise が解決または拒否を実行すると、コールバック関数内のコードと catch がキューに配置されます。

タイマーのコードはマクロタスクに配置され、そのコードはマイクロタスクに配置されます。

マクロタスクとミクロタスクの分割は規定されており、当社が選択するものではありません。

 

マイクロタスクの目的はマクロタスクの前に実行することであるため、マイクロタスクはマクロタスクの前に実行されます。

  console.log("script start")

    // function bar() {
    //   console.log("bar function")
    // }

    // function foo() {
    //   console.log("foo function")
    //   bar()
    // }
    // foo()

    // 定时器
    setTimeout(() => {
      console.log("setTimeout0")
    }, 0)
    setTimeout(() => {
      console.log("setTimeout1")
    }, 0)

    // Promise中的then的回调也会被添加到队列中
    console.log("1111111")
    new Promise((resolve, reject) => {
      console.log("2222222")
      console.log("-------1")
      console.log("-------2")
      resolve()
      console.log("-------3")
    }).then(res => {
      console.log("then传入的回调: res", res)
    })
    console.log("3333333")

    console.log("script end")

コード実行シーケンス - インタビューの質問 1 (重要)

通常の関数と Promise コールバック関数は実行コンテキストで直接実行され、then のコードと Promise の解決コールバックと拒否コールバックのキャッチはマイクロタスクで実行されることに注意してください。

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

最良の方法は、マクロタスクキューとマイクロタスクキューの実行順序を描画することです。

 

 マイクロタスク内にマイクロタスクがある場合、マイクロタスクが早期に完了したかどうかに関係なく、マクロタスクは実行まで遅延されます。マクロタスクの実行中にマイクロタスクを作成した場合(マクロタスクはすでに実行されており、マイクロタスクは終了したとしか言えません)、マクロタスクの完了後、すぐにマイクロタスクの実行に戻ります

 最終的な実行シーケンス:

 

 console.log("script start")

    setTimeout(function () {
      console.log("setTimeout1");
      new Promise(function (resolve) {
        resolve();
      }).then(function () {
        new Promise(function (resolve) {
          resolve();
        }).then(function () {
          console.log("then4");
        });
        console.log("then2");
      });
    });

    new Promise(function (resolve) {
      console.log("promise1");
      resolve();
    }).then(function () {
      console.log("then1");
    });

    setTimeout(function () {
      console.log("setTimeout2");
    });

    console.log(2);

    queueMicrotask(() => {
      console.log("queueMicrotask1")
    });

    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then3");
    });

    console.log("script end")

コード実行シーケンス - 待機コード

then がマクロ タスク内で実行される場合、 then をラップするマクロ タスクが最初に実行され、次に then マイクロタスクが実行されます。

タイマーは、時間と短時間の高度なマクロ タスクにも分割されます。

 await が結果を取得する前に、このスコープ内の後続のコードは実行されず、スキップされます。また、同じスコープ内の await の背後にあるコードは、マイクロタスクに配置されている then のコードと同等です。

 

 console.log("script start")

    function requestData(url) {
      console.log("requestData")
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log("setTimeout")
          resolve(url)
        }, 2000);
      })
    }

    // 2.await/async
    async function getData() {
      console.log("getData start")
      const res = await requestData("why")
      
      console.log("then1-res:", res)
      console.log("getData end")
    }

    getData()
    
    console.log("script end")

    // script start
    // getData start
    // requestData
    // script end

    // setTimeout

    // then1-res: why
    // getData end

コード実行シーケンス - インタビューの質問 2

以下の図で async2() が実行されると、asunc2 関数が呼び出されます。 async2 のみが出力されますが、resolve(unknown) に相当する非表示の return unknown と、console.log('async1 という行が存在します。 end') will then に配置されるのと同じなので、マイクロタスクに追加されます

 注: await async2() のコードは console.log('async1 start') のような通常のコードとして実行されますが、await async2() は関数です。その関数を見つけて、この関数にジャンプして実行する必要があります。中のコード。

 

 

  async function async1 () {
      console.log('async1 start')
      await async2();
      console.log('async1 end')
    }

    async function async2 () {
      console.log('async2')
    }

    console.log('script start')

    setTimeout(function () {
      console.log('setTimeout')
    }, 0)
    
    async1();
    
    new Promise (function (resolve) {
      console.log('promise1')
      resolve();
    }).then (function () {
      console.log('promise2')
    })

    console.log('script end')

例外処理 - デフォルトのエラー処理

ブラウザがエラーを報告した場合、エラーのソース コード行以降のコードは実行されません。とても危険です。

1 つの解決策は、積極的に例外をスローし、 throw キーワードを使用することです。ただし、スロー後のコードは実行されません。利点は、カスタマイズした問題の説明をスローできることです。

 

 // 1.遇到一个错误, 造成后续的代码全部不能执行
    // function foo() {
    //   "abc".filter()

    //   console.log("第15行代码")
    //   console.log("-------")
    // }

    // foo()
    // console.log("+++++++++")

    // const btn = document.querySelector("button")
    // btn.onclick = function() {
    //   console.log("监听btn的点击")
    // }

    // 2.自己封装一些工具
    function sum(num1, num2) {
      if (typeof num1 !== "number") {
        throw "type error: num1传入的类型有问题, 必须是number类型"
      }

      if (typeof num2 !== "number") {
        throw "type error: num2传入的类型有问题, 必须是number类型"
      }

      return num1 + num2
    }

    // 李四调用
    const result = sum(123, 321)

例外処理 -throw 例外をスローします

例外をスローするメッセージは 1 文であり、何の効果もありません。一般に、オブジェクト タイプを使用して例外をスローし、より多くのコンテンツを表示できます。もちろん、クラスをカスタマイズしてエラー メッセージを書き込むこともできます。

システムには、直接使用できる Error クラスもすでに書き込まれています。 

  class HYError {
      constructor(message, code) {
        this.errMessage = message
        this.errCode = code
      }
    }

    // throw抛出一个异常
    // 1.函数中的代码遇到throw之后, 后续的代码都不会执行
    // 2.throw抛出一个具体的错误信息
    function foo() {
      console.log("foo function1")
      // 1.number/string/boolean
      // throw "反正就是一个错误"

      // 2.抛出一个对象
      // throw { errMessage: "我是错误信息", errCode: -1001 }
      // throw new HYError("错误信息", -1001)

      // 3.Error类: 错误函数的调用栈以及位置信息
      throw new Error("我是错误信息")

      console.log("foo function2")
      console.log("foo function3")
      console.log("foo function4")
    }

    function bar() {
      foo()
    }

    bar()

 

例外処理 - 例外をキャッチする方法

一度エラーが発生すると、その後のコードが実行されなくなるのはなぜですか?

これは、エラーが報告された場所からレイヤーごとにエラーが報告され、最終的にブラウザーでエラーがスローされるため、後続のコードが実行を中断するためです。エラーをキャプチャして処理する別のコードを作成すると、その後、後続のコードは引き続き実行できます。

最後に必ず実行します

 function foo() {
      console.log("foo function1")
      // throw new Error("我是错误信息")
      console.log("foo function2")
      console.log("foo function3")
      console.log("foo function4")
    }

    function test() {
      // 自己捕获了异常的话, 那么异常就不会传递给浏览器, 那么后续的代码可以正常执行
      try {
        foo()
        console.log("try后续的代码")
      } catch(error) {
        console.log("catch中的代码")
        // console.log(error)
      } finally {
        console.log("finally代码")
      }
    }

    function bar() {
      test()
    }

    bar()

    console.log("--------")

try catch を記述して例外をキャッチすると、後続のコードは通常どおり実行できるようになります。

 

おすすめ

転載: blog.csdn.net/weixin_56663198/article/details/131728637