ホームページ  >  記事  >  ウェブフロントエンド  >  nodejs のいくつかの主要なノードを理解するためのまとめと共有

nodejs のいくつかの主要なノードを理解するためのまとめと共有

青灯夜游
青灯夜游転載
2022-07-06 20:36:502050ブラウズ

nodejs のいくつかの主要なノードを理解するためのまとめと共有

この記事は、実際の開発と学習における nodejs についての個人的な理解です。将来の参考のためにまとめられています。インスピレーションになれば幸いです。

ノンブロッキング I/O

I/O: 入力/出力、システムの入力と出力。

システムは、人などの個人として理解できます。話すときは出力であり、聞くときは入力です。

ブロッキング I/O とノンブロッキング I/O の違いは、入力から出力までの期間に システムが他の入力を受信できるかどうかです。

次の 2 つの例は、ブロッキング I/O と非ブロッキング I/O が何であるかを示しています:

1. クッキング

nodejs のいくつかの主要なノードを理解するためのまとめと共有

まず第一に、システムの範囲を決定する必要があります。この例では、食堂のおばさんとレストランのウェイターをシステムとみなします。

入力は注文であり、出力は食べ物を提供しています

次に、注文してから料理を提供するまでの間に他の人の注文を受け入れることができるかどうかで、それがブロッキング I/O であるかノンブロッキング I/O であるかを判断できます。

食堂のおばちゃんは、自分が注文している間は他の生徒の注文はできず、生徒が注文して料理を提供し終えてからでないと次の生徒の注文を受けられないので、食堂のおばちゃんはブロック中です。 I/O。

レストランのウェイターの場合、注文後、ゲストが料理を提供する前に次のゲストにサービスを提供できるため、ウェイターはノンブロッキング I/O を利用できます。

2. 家事をする

nodejs のいくつかの主要なノードを理解するためのまとめと共有

洗濯をするとき、洗濯機の横で待つ必要はありません。この時間にそこに行くことができます 床を掃いて机の上を整理します 机が片付いて服を洗ったら、服を干してください 合計で 25 分しかかかりません。

衣類の洗濯は実際にはノンブロッキング I/O です。衣類を洗濯機に入れてから洗濯が終了するまでの間に、他のことを行うことができます。

ノンブロッキング I/O によってパフォーマンスが向上する理由は、不必要な待ち時間を節約できるためです。

ノンブロッキング I/O を理解するための鍵は、:

  • I/O のシステム境界を決定するです。 。これは非常に重要で、上記のレストランの例のように、システムがレストラン全体に拡張された場合、シェフは間違いなく I/O をブロックすることになります。
  • I/O プロセス中に、他の I/O を実行できますか?

nodejs のノンブロッキング I/O

nodejs のノンブロッキング I/O はどのように反映されますか?前述したように、ノンブロッキング I/O を理解する上で重要な点は、最初にシステム境界を決定することです。

node のシステム境界は main thread です。

下のアーキテクチャ図をスレッド保守に従って分割すると、左側の点線がnodejsスレッド、右側の点線がcスレッドになります。

nodejs のいくつかの主要なノードを理解するためのまとめと共有

ここで、nodejs スレッドはデータベースにクエリを実行する必要があります。これは典型的な I/O 操作です。I/O の結果を待たずに処理を続行します。他の操作:​​ 計算のために大量の計算能力を他の C スレッドに分配します。

結果が出るまで待って、nodejs スレッドに返します。結果を取得する前に、nodejs スレッドは他の I/O 操作も実行できるため、ノンブロッキングです。

nodejs thread は、左側の部分がウェイター、C スレッドがシェフに相当します。

つまり、ノードのノンブロッキング I/O は、c のワーカー スレッドを呼び出すことによって完了します。

C スレッドが結果を取得したときに、nodejs スレッドに通知するにはどうすればよいですか?答えは

イベントドリブンです。

イベント駆動型

ブロッキング: プロセスは I/O 中にスリープし、I/O が完了するのを待ってから次の処理に進みます。 step;

Non-blocking: 関数は I/O 中にすぐに戻り、プロセスは I/O の完了を待ちません。

返された結果を確認するには、

イベント駆動型を使用する必要があります。

いわゆる

イベントドリブンは、フロントエンドのクリックイベントと同じものとして理解できます。最初にクリックイベントを書きますが、いつそれが行われるかわかりません。トリガーされたときのみ、メインスレッドにイベントドライバー関数を実行させます。

このモードはオブザーバー モードでもあります。つまり、最初にイベントをリッスンし、トリガーされたときにそれを実行します。

では、イベントドライブを実装するにはどうすればよいでしょうか?答えは

非同期プログラミング です。

非同期プログラミング

上で述べたように、nodejs には多数のノンブロッキング I/O があるため、ノンブロッキング I/O の結果は次のようにする必要があります。

コールバック関数を使用するこの方法は、非同期プログラミングです。たとえば、次のコードはコールバック関数を通じて結果を取得します。

glob(__dirname+'/**/*', (err, res) => {
    result = res
    console.log('get result')
})

コールバック関数の形式指定

nodejs コールバック関数の最初のパラメータは error で、後続のパラメータは result です。どうしてそれをするの?

try {
  interview(function () {
       console.log('smile')
  })
} catch(err) {
    console.log('cry', err)
}

function interview(callback) {
    setTimeout(() => {
        if(Math.random() <p>実行後、エラーは捕捉されず、エラーがグローバルにスローされ、nodejs プログラム全体がクラッシュしました。 </p><p><img src="https://img.php.cn/upload/image/244/886/980/1657110712466688.png" title="1657110712466688.png" alt="nodejs のいくつかの主要なノードを理解するためのまとめと共有"></p><p> は、setTimeout がイベント ループを再度開くため、try catch ではキャプチャされません。イベント ループが開くたびに、コール スタック コンテキストが再生成されます。Try catch は前のイベントに属しますsetTimeout のコールバック関数が実行されると、コール スタックが異なります。この新しいコール スタックには try catch がないため、このエラーはグローバルにスローされ、キャッチできません。詳細については、この記事<a href="https://juejin.cn/post/6995749646366670855" target="_blank" title="https://juejin.cn/post/6995749646366670855">try catchに非同期キューを使用する場合の問題</a>を参照してください。 ######だから何をすべきか?エラーをパラメータとして使用します: </p><pre class="brush:php;toolbar:false">function interview(callback) {
    setTimeout(() => {
        if(Math.random() <p>ただし、これはさらに面倒で、コールバックで判断する必要があるため、成熟した規則が生成されます。最初のパラメータは err です。それが存在しない場合は、実行が成功したことを意味します。 </p><pre class="brush:php;toolbar:false">function interview(callback) {
    setTimeout(() => {
        if(Math.random() <p></p>非同期プロセス制御<h3 data-id="heading-5">
<strong></strong>nodejsのコールバック記述メソッドはコールバック領域を引き起こすだけでなく、</h3>非同期プロセス制御<p>の問題も引き起こします。 <strong></strong>非同期プロセス制御とは、主に、同時実行が発生した場合の同時実行ロジックの処理方法を指します。引き続き上記の例を使用しますが、同僚が 2 社の面接を受ける場合、2 社の面接に成功するまで 3 社目の面接は受けられません。では、このロジックはどのように記述すればよいでしょうか?変数 count をグローバルに 1 つ追加する必要があります: </p><pre class="brush:php;toolbar:false">var count = 0
interview((err) => {
    if (err) {
        return
    }
    count++
    if (count >= 2) {
        // 处理逻辑
    }
})

interview((err) => {
    if (err) {
        return
    }
    count++
    if (count >= 2) {
        // 处理逻辑
    }
})

上記のような記述は非常に面倒で見苦しいです。そのため、promiseやasync/awaitという書き方が後から登場しました。

約束

現在のイベント ループでは結果を取得できませんが、将来のイベント ループでは結果が得られます。悪党の言うこととよく似ています。

約束はクソなだけでなく、状態マシンでもあります:

保留中
  • 履行/解決済み
  • 拒否
  • const pro = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('2')
        }, 200)
    })
    console.log(pro) // 打印:Promise { <pending> }</pending>
then & .catch

解決済み状態の Promise は最初の を呼び出し、次に
  • 拒否された状態の Promise を呼び出します。最初の catch を呼び出します。
  • 拒否状態の後に .catch のない Promise が続くと、ブラウザまたはノード環境でグローバル エラーが発生します。 uncaught は、キャッチされなかったエラーを表します。

nodejs のいくつかの主要なノードを理解するためのまとめと共有then または catch を実行すると、

新しい Promise が返されます

。Promise の最終状態は、コールバック関数の実行結果によって決まります。 :

コールバック関数が常に新しいエラーをスローする場合、Promise は拒否された状態になります
  • コールバック関数が常に return する場合、Promise は拒否された状態になります解決された状態
  • しかし、コールバック関数が常に Promise を返す場合、その
  • promise はコールバック関数によって返される Promise ステータスと一致します
  • function interview() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (Math.random() > 0.5) {
                    resolve('success')
                } else {
                    reject(new Error('fail'))
                }
            })
        })
    }
    
    var promise = interview()
    var promise1 = promise.then(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('accept')
            }, 400)
        })
    })
  • promise1 のステータスは、リターンの Promise のステータス、つまり、リターンの Promise が実行された後の Promise1 のステータスによって決まります。これにはどのような利点があるのでしょうか?これにより、
コールバック地獄の問題を解決できます

var promise = interview()
    .then(() => {
        return interview()
    })
    .then(() => {
        return interview()
    })
    .then(() => {
        return interview()
    })
    .catch(e => {
        console.log(e)
    })
then 返された Promise のステータスが拒否された場合、最初の catch が呼び出され、後続の then は呼び出されません。覚えておいてください: 拒否された呼び出しは最初のキャッチで、解決された呼び出しは最初のキャッチです。

promise は非同期プロセス制御を解決します

promise が地獄のコールバックを解決するだけである場合、promise は小さすぎて過小評価できません。promise の主な機能は非同期プロセス制御を解決することです。プロセス制御の問題。 2 つの企業に同時に面接したい場合:

function interview() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.5) {
                resolve('success')
            } else {
                reject(new Error('fail'))
            }
        })
    })
}

promise
    .all([interview(), interview()])
    .then(() => {
        console.log('smile')
    })
    // 如果有一家公司rejected,就catch
    .catch(() => {
        console.log('cry')
    })

async/await

sync/await とは正確には何ですか:

console.log(async function() {
    return 4
})

console.log(function() {
    return new Promise((resolve, reject) => {
        resolve(4)
    })
})

結果は同じです。言い換えれば、async/await は単なる Promise の構文糖です。

try catch はエラーをキャプチャします

呼び出しスタックに依存します

が、呼び出しスタックより上のエラーのみをキャプチャできることがわかっています。ただし、await を使用すると、コール スタック内のすべての関数のエラーをキャッチできます。 setTimeout などの別のイベント ループの呼び出しスタックでエラーがスローされた場合でも。 インタビュー コードを変換すると、コードが大幅に合理化されていることがわかります。

try {
    await interview(1)
    await interview(2)
    await interview(2)
} catch(e => {
    console.log(e)
})

それが並列タスクの場合はどうなりますか?

await Promise.all([interview(1), interview(2)])

イベント ループ

nodejs のノンブロッキング I/0 のため、I/O 結果を取得するにはイベント駆動型メソッドを使用する必要があります。コールバック関数などの非同期プログラミングを使用する必要があります。では、結果を得るためにこれらのコールバック関数を実行するにはどうすればよいでしょうか?次に、イベントループを使用する必要があります。

イベント ループは、nodejs のノンブロッキング I/O 機能を実現するための重要な基盤です。ノンブロッキング I/O とイベント ループは両方とも、

libuv

c ライブラリによって提供される機能です。 。 <p><img src="https://img.php.cn/upload/image/722/472/129/165711073676826nodejs%20%E3%81%AE%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E4%B8%BB%E8%A6%81%E3%81%AA%E3%83%8E%E3%83%BC%E3%83%89%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81%E3%81%A8%E5%85%B1%E6%9C%89" title="165711073676826nodejs のいくつかの主要なノードを理解するためのまとめと共有" alt="nodejs のいくつかの主要なノードを理解するためのまとめと共有"></p> <p>代码演示:</p> <pre class="brush:php;toolbar:false">const eventloop = {     queue: [],     loop() {         while(this.queue.length) {             const callback = this.queue.shift()             callback()         }         setTimeout(this.loop.bind(this), 50)     },     add(callback) {         this.queue.push(callback)     } } eventloop.loop() setTimeout(() =&gt; {     eventloop.add(() =&gt; {         console.log('1')     }) }, 500) setTimeout(() =&gt; { eventloop.add(() =&gt; { console.log('2') }) }, 800)</pre> <p><code>setTimeout(this.loop.bind(this), 50)保证了50ms就会去看队列中是否有回调,如果有就去执行。这样就形成了一个事件循环。

当然实际的事件要复杂的多,队列也不止一个,比如有一个文件操作对列,一个时间对列。

const eventloop = {
    queue: [],
    fsQueue: [],
    timerQueue: [],
    loop() {
        while(this.queue.length) {
            const callback = this.queue.shift()
            callback()
        }
        this.fsQueue.forEach(callback => {
            if (done) {
                callback()
            }
        })
        setTimeout(this.loop.bind(this), 50)
    },
    add(callback) {
        this.queue.push(callback)
    }
}

总结

首先我们弄清楚了什么是非阻塞I/O,即遇到I/O立刻跳过执行后面的任务,不会等待I/O的结果。当I/O处理好了之后就会调用我们注册的事件处理函数,这就叫事件驱动。实现事件驱动就必须要用异步编程,异步编程是nodejs中最重要的环节,它从回调函数到promise,最后到async/await(使用同步的方法写异步逻辑)。

更多node相关知识,请访问:nodejs 教程

以上がnodejs のいくつかの主要なノードを理解するためのまとめと共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。