Node.js のコールバックとコード設計パターンの分析非同期プログラミング_node.js
NodeJS の最大のセールスポイントであるイベント メカニズムと非同期 IO は、開発者にとっては透過的ではありません。開発者はこのセールスポイントを利用するにはコードを非同期で記述する必要がありますが、これは一部の NodeJS 反対派から批判されています。しかし、何はともあれ、非同期プログラミングは確かに NodeJS の最大の特徴です。非同期プログラミングをマスターしなければ、本当に NodeJS を学んだとは言えません。この章では、非同期プログラミングに関するさまざまな知識を紹介します。
コードでは、非同期プログラミングを直接表現するのはコールバックです。非同期プログラミングはコールバックに依存しますが、コールバックを使用したからといってプログラムが非同期になるとは言えません。まず次のコードを見てみましょう。
function heavyCompute(n, callback) { var count = 0, i, j; for (i = n; i > 0; --i) { for (j = n; j > 0; --j) { count += 1; } } callback(count); } heavyCompute(10000, function (count) { console.log(count); }); console.log('hello');
100000000 hello
ご覧のとおり、上記のコードのコールバック関数は後続のコードの前に実行されます。 JS 自体は単一のスレッドで実行され、コードの一部の実行が終了する前に他のコードを実行することは不可能であるため、非同期実行の概念はありません。
ただし、関数の実行内容が、別のスレッドまたはプロセスを作成し、JS メイン スレッドと並行して何かを実行し、それが完了したときに JS メイン スレッドに通知することである場合、状況は異なります。次のコードを見てみましょう。
setTimeout(function () { console.log('world'); }, 1000); console.log('hello');
hello world
今回は、後続のコードの後にコールバック関数が実行されていることがわかります。前述したように、JS 自体はシングルスレッドであり、非同期実行はできません。そのため、setTimeout などの JS 仕様外の実行環境が提供する特別な関数は、並列スレッドを作成してすぐに返すことであり、 JS マスターからプロセスは、並列プロセスから通知を受信した後、後続のコードを実行し、コールバック関数を実行できます。このような関数には、setTimeout や setInterval などの一般的な関数に加えて、fs.readFile などの NodeJS によって提供される非同期 API も含まれます。
さらに、JS は単一のスレッドで実行されるという事実に戻ります。これにより、JS はコードの一部を実行する前に、コールバック関数を含む他のコードを実行できないことが決まります。つまり、並列スレッドが作業を完了し、JS メイン スレッドにコールバック関数を実行するように通知した場合でも、JS メイン スレッドがアイドル状態になるまでコールバック関数は実行を開始しません。以下はその一例です。
function heavyCompute(n) { var count = 0, i, j; for (i = n; i > 0; --i) { for (j = n; j > 0; --j) { count += 1; } } } var t = new Date(); setTimeout(function () { console.log(new Date() - t); }, 1000); heavyCompute(50000);
8520
ご覧のとおり、JS メイン スレッドが他のコードの実行でビジー状態だったために、1 秒後に呼び出されるはずだったコールバック関数の実際の実行時間は大幅に遅れました。
コード設計パターン
非同期プログラミングには、同じ機能を実現するために、同期モードと非同期モードで記述されるコードは大きく異なります。よくあるパターンをいくつか紹介します。
関数の戻り値
同期モードでは、ある関数の出力を別の関数の入力として使用することが非常に一般的な要件であり、コードは通常次のように記述されます。
var output = fn1(fn2('input')); // Do something.
fn2('input', function (output2) { fn1(output2, function (output1) { // Do something. }); });
配列を走査するとき、関数を使用してデータ メンバーに対して何らかの処理を順番に実行することも一般的な要件です。関数が同期的に実行される場合、通常は次のコードが記述されます:
var len = arr.length, i = 0; for (; i < len; ++i) { arr[i] = sync(arr[i]); } // All array items have processed.
(function next(i, len, callback) { if (i < len) { async(arr[i], function (value) { arr[i] = value; next(i + 1, len, callback); }); } else { callback(); } }(0, arr.length, function () { // All array items have processed. }));
配列メンバーを並列処理できるが、後続のコードでは実行前にすべての配列メンバーを処理する必要がある場合、非同期コードは次の形式に調整されます:
(function (i, len, count, callback) { for (; i < len; ++i) { (function (i) { async(arr[i], function (value) { arr[i] = value; if (++count === len) { callback(); } }); }(i)); } }(0, arr.length, 0, function () { // All array items have processed. }));
JS 自体によって提供される例外のキャッチおよび処理メカニズム - try..catch.. は、同期的に実行されるコードにのみ使用できます。以下に例を示します。
function sync(fn) { return fn(); } try { sync(null); // Do something. } catch (err) { console.log('Error: %s', err.message); }
Error: object is not a function
function async(fn, callback) { // Code execution path breaks here. setTimeout(function () { callback(fn()); }, 0); } try { async(null, function (data) { // Do something. }); } catch (err) { console.log('Error: %s', err.message); }
因为代码执行路径被打断了,我们就需要在异常冒泡到断点之前用 try 语句把异常捕获住,并通过回调函数传递被捕获的异常。于是我们可以像下边这样改造上边的例子。
function async(fn, callback) { // Code execution path breaks here. setTimeout(function () { try { callback(null, fn()); } catch (err) { callback(err); } }, 0); } async(null, function (err, data) { if (err) { console.log('Error: %s', err.message); } else { // Do something. } });
Error: object is not a function
可以看到,异常再次被捕获住了。在 NodeJS 中,几乎所有异步 API 都按照以上方式设计,回调函数中第一个参数都是 err。因此我们在编写自己的异步函数时,也可以按照这种方式来处理异常,与 NodeJS 的设计风格保持一致。
有了异常处理方式后,我们接着可以想一想一般我们是怎么写代码的。基本上,我们的代码都是做一些事情,然后调用一个函数,然后再做一些事情,然后再调用一个函数,如此循环。如果我们写的是同步代码,只需要在代码入口点写一个 try 语句就能捕获所有冒泡上来的异常,示例如下。
function main() { // Do something. syncA(); // Do something. syncB(); // Do something. syncC(); } try { main(); } catch (err) { // Deal with exception. }
但是,如果我们写的是异步代码,就只有呵呵了。由于每次异步函数调用都会打断代码执行路径,只能通过回调函数来传递异常,于是我们就需要在每个回调函数里判断是否有异常发生,于是只用三次异步函数调用,就会产生下边这种代码。
function main(callback) { // Do something. asyncA(function (err, data) { if (err) { callback(err); } else { // Do something asyncB(function (err, data) { if (err) { callback(err); } else { // Do something asyncC(function (err, data) { if (err) { callback(err); } else { // Do something callback(null); } }); } }); } }); } main(function (err) { if (err) { // Deal with exception. } });
可以看到,回调函数已经让代码变得复杂了,而异步方式下对异常的处理更加剧了代码的复杂度。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









ファイルのアップロードをどのように処理するか?次の記事では、Express を使用してノード プロジェクトでファイルのアップロードを処理する方法を紹介します。

この記事では、Node のプロセス管理ツール「pm2」について説明し、pm2 が必要な理由、pm2 のインストール方法と使用方法について説明します。皆様のお役に立てれば幸いです。

ピン張りのノードの詳細な説明とインストールガイドこの記事では、ピネットワークのエコシステムを詳細に紹介します - PIノードは、ピン系生態系における重要な役割であり、設置と構成の完全な手順を提供します。 Pinetworkブロックチェーンテストネットワークの発売後、PIノードは多くの先駆者の重要な部分になり、テストに積極的に参加し、今後のメインネットワークリリースの準備をしています。まだピン張りのものがわからない場合は、ピコインとは何かを参照してください。リストの価格はいくらですか? PIの使用、マイニング、セキュリティ分析。パインワークとは何ですか?ピン競技プロジェクトは2019年に開始され、独占的な暗号通貨PIコインを所有しています。このプロジェクトは、誰もが参加できるものを作成することを目指しています

クイック アプリケーション: PHP の実践的な開発ケース分析 複数ファイルの非同期 HTTP ダウンロード インターネットの発展に伴い、ファイル ダウンロード機能は多くの Web サイトやアプリケーションの基本的なニーズの 1 つになりました。複数のファイルを同時にダウンロードする必要があるシナリオでは、従来の同期ダウンロード方法は非効率的で時間がかかることがよくあります。このため、PHP を使用して HTTP 経由で複数のファイルを非同期にダウンロードするソリューションがますます一般的になってきています。この記事では、実際の開発事例を通して、PHP 非同期 HTTP の使用方法を詳しく分析します。

認証は、Web アプリケーションの最も重要な部分の 1 つです。このチュートリアルでは、トークンベースの認証システムと、それが従来のログイン システムとどのように異なるかについて説明します。このチュートリアルを終えると、Angular と Node.js で書かれた完全に動作するデモが表示されます。従来の認証システム トークンベースの認証システムに進む前に、従来の認証システムを見てみましょう。ユーザーはログイン フォームにユーザー名とパスワードを入力し、[ログイン] をクリックします。リクエストを行った後、データベースにクエリを実行してバックエンドでユーザーを認証します。リクエストが有効な場合、データベースから取得したユーザー情報を使用してセッションが作成され、セッション情報が応答ヘッダーで返され、セッション ID がブラウザに保存されます。対象となるアプリケーションへのアクセスを提供します。

インターネットの継続的な発展と普及に伴い、電子メールは人々の生活や仕事に欠かせないものとなり、SMTP (Simple Mail Transfer Protocol) は電子メール送信のための重要なプロトコルの 1 つです。 PHP の非同期ネットワーク通信フレームワークとして、Swoole は非同期 SMTP 操作を十分にサポートし、電子メール送信をより効率的かつ安定させます。この記事では、Swoole が同期の使用を含む非同期 SMTP 操作をどのようにサポートするかを紹介します。

インターネット ビジネスの量が増え続けるにつれて、高い同時実行性と高性能に対する要求がますます高まっており、PHP のネットワーク通信フレームワークとしての Swoole が開発者の間でますます支持されています。その中でも、Swoole は、より一般的なアプリケーション シナリオの 1 つである非同期 AMQP をサポートしています。それでは、Swoole が非同期 AMQP 操作をどのようにサポートするかを見てみましょう。まず、AMQP とは何かを明確にする必要があります。 AMQP (AdvancedMessageQueuingProtocol) アドバンスト

同時プログラミングと非同期プログラミング 同時プログラミングは、同時に実行される複数のタスクを扱います。非同期プログラミングは、タスクがスレッドをブロックしない同時プログラミングの一種です。 asyncio は Python の非同期プログラミング用のライブラリで、プログラムがメイン スレッドをブロックせずに I/O 操作を実行できるようにします。イベント ループ asyncio の中核は、I/O イベントを監視し、対応するタスクをスケジュールするイベント ループです。コルーチンの準備が完了すると、イベント ループは I/O 操作を待つまでそのコルーチンを実行します。その後、コルーチンを一時停止し、他のコルーチンの実行を継続します。コルーチン コルーチンは、実行を一時停止および再開できる関数です。 asyncdef キーワードは、コルーチンの作成に使用されます。コルーチンは await キーワードを使用して、I/O 操作が完了するのを待ちます。 asyncio の次の基本
