1 つの記事で Node.js の非同期プログラミングを学ぶ

1 つの記事で Node.js の非同期プログラミングを学ぶ

目次 はじめに 同期 非同期とブロッキング JavaScript のノンブロッキング コールバック コールバック関数のエラー処理 コールバック地獄 ES6 の Promise Promise とは Promise の機能 Promise の利点 Promise の欠点 Promise の使い方 Promise の実行順序 async と await async の実行順序 async の機能のまとめ

導入

JavaScript はデフォルトでシングルスレッドであるため、コードは並列実行するための新しいスレッドを作成できません。しかし、ブラウザで最初に実行された JavaScript の場合、シングルスレッドの同期実行環境では、ページのクリックやマウスの移動など、ユーザー応答機能のニーズを満たすことができませんでした。そのため、ブラウザは、JavaScript がコールバック方式でページ要求イベントに非同期的に応答できるようにする一連の API を実装しました。

さらに、NodeJS は非ブロッキング I/O を導入し、非同期の概念をファイル アクセスやネットワーク呼び出しなどに拡張しました。

今日は、さまざまな非同期プログラミングの利点、欠点、開発の傾向について詳しく見ていきます。

同期、非同期、ブロッキング、非ブロッキング

Node.js の非同期プログラミングについて説明する前に、混同されやすい概念、つまり同期、非同期、ブロッキング、非ブロッキングについて説明しましょう。

いわゆるブロッキングとノンブロッキングとは、プロセスまたはスレッドが操作を実行したり、データの読み書きをするときに待機する必要があるかどうか、および待機プロセス中に他の操作を実行できるかどうかを指します。

待機が必要で、スレッドまたはプロセスが待機プロセス中に他の操作を実行できず、ただ待機することしかできない場合、この操作はブロックされていると言います。

逆に、プロセスまたはスレッドが操作を実行したり、データの読み取りや書き込みを行ったりしながら他の操作を実行できる場合、その操作は非ブロッキングであると言えます。

同期と非同期は、データにアクセスする方法を指します。同期とは、データをアクティブに読み取る必要があることを意味し、この読み取りプロセスはブロッキングまたは非ブロッキングになります。非同期とは、データを積極的に読み取る必要がなく、受動的な通知であることを意味します。

明らかに、JavaScript のコールバックは受動的な通知であり、非同期呼び出しと呼ぶことができます。

JavaScript のコールバック

JavaScript のコールバックは、非同期プログラミングの非常に典型的な例です。

document.getElementById('button').addEventListener('click', () => {
 console.log('ボタンがクリックされました!');
})

上記のコードでは、ボタンにクリック イベント リスナーを追加しました。クリック イベントがリッスンされると、コールバック関数がトリガーされ、対応する情報が出力されます。

コールバック関数は、addEventListener にパラメーターとして渡され、イベントがトリガーされたときにのみ呼び出される点を除いて、通常の関数と同じです。

前回の記事で説明した setTimeout と setInterval は、実際には非同期コールバック関数です。

コールバック関数でのエラー処理

Node.js でコールバックエラー情報を処理するにはどうすればよいですか? Nodejs は非常に巧妙な方法を使用します。Nodejs では、コールバック関数の最初のパラメータはエラー オブジェクトです。このエラー オブジェクトが存在するかどうかを判断することで、対応するエラー処理を実行できます。

fs.readFile('/文件.json', (err, data) => {
 (エラー!== null)の場合{
 //エラーを処理する console.log(err)
 戻る
 }

 // エラーがなければデータを処理します。
 コンソール.log(データ)
})

コールバック地獄

JavaScript コールバックは優れていますが、同期処理の問題を効果的に解決します。しかし残念なことに、次のステップを実行するためにコールバック関数の戻り値に頼る必要がある場合、このコールバック地獄に陥ってしまいます。

これをコールバック地獄と呼ぶのは少し大げさですが、コールバック関数の問題をある側面から反映しているとも言えます。

fs.readFile('/a.json', (err, データ) => {
 (エラー!== null)の場合{
 fs.readFile('/b.json',(err,data) => {
  //コールバック内のコールバック
 })
 }
})

どうすれば解決できるでしょうか?

ES6 で Promise が導入され、ES2017 で Async/Await が導入されたので、この問題を解決できると心配する必要はありません。

ES6 の Promise

約束とは何ですか?

Promise は非同期プログラミングのソリューションであり、「コールバック関数とイベント」という従来のソリューションよりも合理的で強力です。

いわゆる Promise は、将来終了するイベント (通常は非同期操作) の結果を格納する単なるコンテナーです。

構文的には、Promise は非同期操作に関するメッセージを取得できるオブジェクトです。

プロミスの特徴

Promise には 2 つの特性があります。

オブジェクトの状態は外界の影響を受けません。

Promise オブジェクトは非同期操作を表し、Pending (進行中)、Resolved (完了、Fulfilled とも呼ばれる)、および Rejected (失敗) の 3 つの状態を持ちます。

非同期操作の結果のみが現在の状態を決定し、他の操作ではこの状態を変更できません。

一度状態が変化すると、再び変化することはなく、いつでも結果を得ることができます。

Promise オブジェクトの状態が変化する可能性は、Pending から Resolved へ、または Pending から Rejected への 2 つだけです。

これはイベントとは全く違います。イベントは聞き逃したら何も得られないという特性があります。

約束の利点

Promise は、ネストされたコールバック関数を回避して、非同期操作を同期操作の形式で表現します。

Promise オブジェクトは、非同期操作の制御を容易にする統一されたインターフェースを提供します。

約束のデメリット

  1. Promise はキャンセルできません。作成されるとすぐに実行され、途中でキャンセルすることはできません。
  2. コールバック関数が設定されていない場合、Promise 内でスローされたエラーは外部に反映されません。
  3. 保留中状態の場合、プロジェクトが現在どの段階にあるか (開始したばかりか、完了間近か) を知ることはできません。

Promiseの使用法

Promise オブジェクトは、Promise インスタンスを生成するコンストラクターです。

var promise = new Promise(function(resolve, reject) { 
// ... コード 
if (/* 非同期操作が成功しました*/){ 
解決(値); 
} そうでない場合は{ 拒否(エラー); } 
}
);

Promise は then 操作に接続でき、then 操作は 2 つの関数パラメータに接続できます。最初の関数パラメータは Promise の構築時に解決された値であり、2 番目の関数パラメータは Promise を拒否したエラーです。

promise.then(関数(値) { 
// 成功 
}, 関数(エラー) { 
// 失敗 }
);

具体的な例を見てみましょう。

関数タイムアウト(ms){
 新しい Promise を返します(((resolve, reject) => {
  setTimeout(解決、ミリ秒、'完了');
 }))
}

タイムアウト(100)。その後(値 => console.log(値));

Promise で setTimeout メソッドが呼び出され、固定の時間に resolve メソッドがトリガーされ、パラメータ done が渡されます。

最後に、プログラムは done を出力します。

約束の実行順序

Promise が作成されると、すぐに実行されます。ただし、Promise.then のメソッドは、呼び出しサイクルが経過するまで待機してから再度呼び出します。次の例を見てみましょう。

promise = new Promise(((resolve, deny) => { とする
 console.log('ステップ1');
 解決する();
}));

promise.then(() => {
 console.log('ステップ3');
});

console.log('ステップ2');

出力:
ステップ1
ステップ2
ステップ3

非同期と待機

もちろん、Promise は素晴らしいです。コールバック地獄をチェーン呼び出しに変換します。 then を使用して複数の Promise を接続し、前の Promise の解決の結果が次の Promise の then のパラメーターになります。

チェーンコールの欠点は何ですか?

たとえば、Promise から値を解決する場合、この値に基づいて何らかのビジネス ロジック処理を実行する必要があります。

このビジネス ロジックが非常に長い場合は、次に長いビジネス ロジック コードを記述する必要があります。これにより、コードが非常に冗長に見えます。

では、promise で解決の結果を直接返す方法はありますか?

答えは待ってください。

promise の前に await がある場合、呼び出しコードは、promise が解決または拒否されるまで停止します。

await は async 関数内に配置する必要があることに注意してください。async と await の例を見てみましょう。

定数 logAsync = () => {
 新しいPromiseを返します(resolve => {
 setTimeout(() => 解決('小馬歌'), 5000)
 })
}

上記では、Promise を返す logAsync 関数を定義しました。Promise は setTimeout を使用して解決するため、非同期であるとみなすことができます。

await を使用してresolveの値を取得するには、それをasync関数に配置する必要があります。

定数doSomething = async() => {
 const 解決値 = logAsync() を待機します。
 コンソールにログ出力します。
}

非同期実行順序

Await は実際には promise の解決結果を待ちます。上記の例を組み合わせてみましょう。

定数 logAsync = () => {
 新しいPromiseを返します(resolve => {
  setTimeout(() => 解決('小馬歌'), 1000)
 })
}

定数doSomething = async() => {
 const 解決値 = logAsync() を待機します。
 コンソールにログ出力します。
}

console.log('前')
何かを実行します();
console.log('後')

上記の例の出力は次のようになります。

前に

リトル・マ

ご覧のとおり、aysnc は非同期で実行され、そのシーケンスは現在のサイクルの後にあります。

非同期の機能

後続の関数が明示的に Promise を返さない場合でも、Async は後続のすべての関数を Promise に変換します。

定数asyncReturn = async() => {
 '非同期戻り' を返す
}

asyncReturn().then(console.log)

then の後に続くことができるのは Promise のみであるため、async は通常の関数を Promise にカプセル化していることがわかります。

定数asyncReturn = async() => {
 Promise.resolve('async return') を返します。
}

asyncReturn().then(console.log)

要約する

Promise は、コールバック内のコールバックを then のチェーン呼び出しに書き換えることで、コールバック地獄を回避します。

しかし、チェーン呼び出しは読み取りやデバッグには不便です。そこで async と await が登場しました。

async と await は、チェーン呼び出しをプログラムの順次実行に似た構文に変更し、理解とデバッグを容易にします。

比較してみましょう。まず Promise の使用法を見てみましょう。

定数 getUserInfo = () => {
 return fetch('/users.json') // ユーザーリストを取得します。then(response => response.json()) // JSON を解析します。
 .then(users => users[0]) // 最初のユーザーを選択します。then(user => fetch(`/users/${user.name}`)) // ユーザーデータを取得します。then(userResponse => userResponse.json()) // JSON を解析します。
}

ユーザー情報を取得する()

これを async と await で書き直します。

const getUserInfo = 非同期() => {
 const response = await fetch('/users.json') // ユーザーリストを取得 const users = await response.json() // JSONを解析
 const user = users[0] // 最初のユーザーを選択 const userResponse = await fetch(`/users/${user.name}`) // ユーザーデータを取得 const userData = await userResponse.json() // JSONを解析
 ユーザーデータを返す
}

ユーザー情報を取得する()

ビジネスロジックがより明確になっていることがわかります。同時に、多くの中間値が得られるため、デバッグが容易になります。

これで、Node.js での非同期プログラミングの詳細な理解に関するこの記事は終了です。より関連性の高い Node.js 非同期プログラミング コンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • シングルスレッドJavaScriptにおける非同期処理実装の詳細な説明
  • JSシングルスレッド非同期IOコールバックの特性を分析する
  • Javascript 非同期プログラミング: Promise を本当に理解していますか?
  • JavaScript 非同期プログラミングにおける Promise の初期の使用法の詳細な説明
  • JS 非同期実行の原則とコールバックの詳細
  • 最新の JavaScript で非同期タスクを書く方法
  • Node.js の非同期ジェネレータと非同期反復の詳細な説明
  • Node.js における非同期プログラミングの知識ポイントの詳細な説明
  • JS の 3 つの主要な問題、非同期性とシングルスレッドについて簡単に説明します。

<<:  MySQL 5.7 zip版(zip版)のインストールと設定手順の詳細

>>:  Linux でも利用できる人気の Windows アプリ 10 選

推薦する

Dockerイメージのローカル移行の実装

最近 Docker を勉強しているのですが、よく問題に遭遇します。Docker イメージをダウンロー...

NavicatがMySQLに接続すると、10060、1045エラーとmy.iniの場所が報告されます。

Navicat は、データベースに接続するときにエラー 10060 および 1045 を報告します...

MySQL エラー 1290 (HY000) の解決方法

私は長い間問題に取り組み、文法上の問題を何度も確認しました。しかし、後でネットで調べてみたら、突然理...

Vue が価格カレンダー効果を実現

この記事では、価格カレンダー効果を実現するためのVueの具体的なコードを例として紹介します。具体的な...

MySQLでカーソルを宣言する方法

MySQL でカーソルを宣言する方法: 1. 変数とカーソルを宣言する 結果をvarchar(300...

CSS3 の transition、transform、translate の違いと機能の簡単な分析

変換して翻訳するTransform は、変換と変形を意味します。他の幅属性や高さ属性と同様に、CSS...

MySQLトランザクションとSpring分離レベルの実装原理の詳細な説明

1. トランザクションはACID特性を持つ原子性: トランザクションは、トランザクションによって分割...

シンプルなドラッグ効果を実現するJavaScript

この記事では、簡単なドラッグ効果を実現するためのJavaScriptの具体的なコードを参考までに紹介...

VueにおけるAxios非同期通信の詳細な説明

1. まず、インタラクティブに使用するための .json ファイルを作成します。json データ形式...

MySQL マスタースレーブレプリケーションの原理と注意点

前面に書かれた最近、Mycat で特別なトピックを書いています。最近、多くの友人が面接に出かけている...

Linux での JDK のインストール (OpenJDK のアンインストールを含む) の概要

1. openjdkを表示する rpm -qa|grep jdk 2. openjdk を削除します...

Alibaba Cloud Server への Web プロジェクトのデプロイについて (5 つの手順)

1.まずAlibaba Cloudのウェブサイトにログインしてアカウントを登録し、サーバータイプを...

CentOS7仮想マシンで固定IPアドレスを設定する方法

私の開発環境は、VMWare 仮想マシンに CentOS をインストールし、ホスト ファイルにインタ...

Linux システムで IPv6 をサポートするように Nginx を設定する方法

1. 既存のnginxがipv6をサポートしているかどうかを確認する既存の nginx が ipv6...

時間のかかるDockerエラーのトラブルシューティングプロセス記録

目次起源環境情報トラブルシューティングのプロセス要約する起源顧客は CentOS をベースにしたカス...