JavaScriptコールバック関数の詳細な理解

JavaScriptコールバック関数の詳細な理解

序文

JavaScript コールバック関数は、成功する JavaScript 開発者になるために理解しなければならない重要な概念です。しかし、この記事を読めば、コールバック メソッドを使用してこれまで直面したすべての障害を克服できるようになると確信しています。

始める前に、まず関数についての理解がしっかりしていることを確認しましょう。

クイックレビュー: JavaScript 関数

関数とは何ですか?

関数とは、特定のタスクを実行するためのコードのグループを内部に持つ論理的な構成要素です。関数を使用すると、コードをより体系的に記述できるため、デバッグやメンテナンスが容易になります。関数を使用するとコードの再利用も可能になります。

同じコードを何度も記述するのではなく、関数を一度定義して、必要なときに呼び出します。

関数を宣言する

それでは、JavaScript で関数を宣言する方法を見てみましょう。

  1. 関数のコンストラクターの使用: この方法では、関数は「関数」のコンストラクターを使用して作成されます。技術的には、この方法は、関数式構文と関数宣言ステートメント構文を使用して関数を宣言するよりも効率が低くなります。
  2. 関数式の使用: 一般的に、このアプローチは変数の割り当てと同じです。つまり、関数本体は式として扱われ、その式が変数に割り当てられます。この構文を使用して定義される関数は、名前付き関数または匿名関数にすることができます。
    名前のない関数は匿名関数と呼ばれます。匿名関数は自己呼び出し型であり、自動的に自分自身を呼び出します。この動作は、即時呼び出し関数式 (IIFE) とも呼ばれます。
  3. 関数宣言の使用: この方法は、JavaScript で一般的に使用される古い方法です。キーワード「function」の後に関数の名前を指定する必要があります。その後、関数が複数のパラメーターまたは引数を受け入れる場合は、それらについても言及する必要があります。ただし、この部分は完全にオプションです。

関数本体内では、関数は呼び出し元に値を返す必要があります。 return ステートメントに遭遇すると、関数は実行を停止します。関数内では、パラメータはローカル変数として機能します。

同様に、関数内で宣言された変数はその関数に対してローカルです。ローカル変数はその関数内でのみアクセス可能なので、同じ名前の変数を異なる関数で簡単に使用できます。

関数の呼び出し

次のいずれかの場合、以前に宣言された関数が呼び出されます。

  • イベントが発生すると、たとえば、ユーザーがボタンをクリックしたり、ドロップダウン リストからオプションを選択したりします。
  • 関数が JavaScript コードから呼び出されたとき。
  • この関数は自動的に呼び出すことができます。これについては匿名関数式ですでに説明しました。

() 演算子は関数を呼び出します。

コールバック関数とは何ですか?

MDN によると、コールバック関数は、別の関数にパラメーターとして渡される関数であり、その後、コールバック関数は、何らかの操作を完了するために外側の関数内で呼び出されます。

人間的な言葉で説明すると、コールバックは別の関数の実行が終了した直後に実行される関数です。コールバック関数は、別の JavaScript 関数に引数として渡される関数です。このコールバック関数は、渡された関数内で実行されます。

関数は、JavaScript ではファーストクラス オブジェクトと見なされます。ファーストクラス オブジェクトとは、言語内の他のエンティティと同じように扱うことができる数値、関数、または変数を意味します。関数はファーストクラスオブジェクトであるため、変数として他の関数に渡すことができ、他の関数から返すこともできます。

このような操作を実行できる関数は高階関数と呼ばれます。コールバック関数は実際にはパターンです。 「パターン」という言葉は、ソフトウェア開発における一般的な問題を解決するための実証済みのアプローチを指します。コールバックパターンとしてはコールバック関数を使用するのが最適です。

なぜコールバックが必要なのでしょうか?

クライアント側の JavaScript はブラウザ内で実行され、ブラウザのメインプロセスはシングルスレッドのイベント ループです。シングルスレッドのイベント ループで長時間実行される操作を実行しようとすると、プロセスがブロックされます。これは技術的には問題です。なぜなら、プロセスは操作が完了するのを待っている間に他のイベントの処理を停止するからです。

たとえば、アラート ステートメントは、ブラウザーの JavaScript のブロック コードの 1 つと見なされます。アラートを実行すると、アラート ダイアログ ウィンドウを閉じるまでブラウザーでの操作ができなくなります。長時間実行される操作がブロックされるのを防ぐために、コールバックを使用します。

コールバックがどのような状況で使用されるかを正確に理解できるように、さらに詳しく見てみましょう。

メッセージを取得して表示する機能

上記のコード スニペットでは、最初に getMessage() 関数が実行され、次に displayMessage() が実行されます。どちらもブラウザのコンソール ウィンドウにメッセージを表示し、すぐに実行されます。

場合によっては、一部のコードはすぐに実行されません。たとえば、getMessage() 関数が API 呼び出しを実行すると仮定すると、サーバーにリクエストを送信し、応答を待つ必要があります。こういう時、私たちは何をすべきでしょうか?

コールバック関数の使い方

JavaScript コールバック関数の構文を説明するよりも、前の例でコールバック関数を実装する方がよいと考えました。変更されたコード スニペットは以下のスクリーンショットに示されています。

コールバック関数によるメッセージの表示

コールバック関数を使用するには、結果がすぐに表示されない何らかのタスクを実行する必要があります。この動作をシミュレートするには、JavaScript の setTimeout() 関数を使用します。この関数は 2 秒間一時停止し、その後コンソール ウィンドウに「Hi, there」というメッセージを表示します。

「表示されたメッセージ」はブラウザのコンソール ウィンドウに表示されます。この場合、まず getMessage() 関数を待つ必要があります。この関数を正常に実行した後、displayMessage() 関数を実行します。

コールバックの仕組み

前の例の舞台裏で何が起こったのか説明しましょう。

前の例からわかるように、getMessage() 関数では 2 つのパラメータを渡しました。最初の引数はブラウザのコンソール ウィンドウに表示される msg 変数であり、2 番目の引数はコールバック関数です。

ここで、なぜコールバック関数をパラメータとして渡すのか疑問に思うかもしれません。コールバック関数を実装するには、関数を別の関数にパラメータとして渡す必要があるからです。

getMessage() がタスクを完了したら、コールバック関数を呼び出します。その後、getMessage() 関数が呼び出されると、参照はコールバック関数である displayMessage() 関数に渡されます。

getMessage() 関数が呼び出されるときは、その参照のみが displayMessage() 関数に渡されることに注意してください。そのため、関数呼び出し演算子 () 記号がその横に表示されません。

Javascript コールバックは非同期ですか?

JavaScript はシングルスレッドのスクリプト言語と見なされます。シングルスレッドとは、JavaScript が一度に 1 つのコード ブロックを実行することを意味します。 JavaScript が 1 つのブロックを実行中の間は、次のブロックに移動することはできません。

言い換えれば、JavaScript コードは本質的に常にブロックするものであると考えることができます。しかし、このブロッキング特性により、特定のタスクを実行した後すぐに結果を得る方法がない場合には、コードを記述できなくなります。

私が話しているタスクには、次のような状況が含まれます。

  • データは、特定のエンドポイントへの API 呼び出しによって取得されます。
  • ネットワーク要求を送信して、リモート サーバーからいくつかのリソース (テキスト ファイル、イメージ ファイル、バイナリ ファイルなど) を取得します。

このような状況に対処するには、非同期コードを記述する必要があり、コールバック関数はこれらの状況に対処する 1 つの方法です。したがって、本質的には、コールバック関数は非同期です。

Javascript コールバック地獄

複数の非同期関数が次々に実行されると、コールバック地獄が発生します。運命のピラミッドとしても知られています。

すべての Github ユーザーのリストを取得したいとします。次に、ユーザーの中から JavaScript ライブラリへの主要な貢献者を検索します。次に、Users 内の John という名前の人物の詳細を取得します。

コールバックを利用してこの機能を実装するには、コードは次のようになります。

http.get('https://api.github.com/users', 関数(users) {
  /* すべてのユーザーを表示 */
  コンソールにユーザーをログ出力します。
  http.get('https://api.github.com/repos/javascript/contributors?q=contributions&order=desc', function(貢献者) {
  /* すべてのトップ貢献者を表示 */
    console.log(貢献者);
    http.get('https://api.github.com/users/Jhon', 関数(userData) {
    /* ユーザー名 'Jhon' のユーザーを表示します */
      コンソールにユーザーデータをログ出力します。
    });
  });
});

上記のコード スニペットから、コードの理解、保守、変更がより困難になることがわかります。これはコールバック関数のネストによって発生します。

コールバック地獄を回避するにはどうすればよいでしょうか?

以下に示すように、コールバック地獄を回避するために使用できるテクニックがいくつかあります。

  1. プロミスの使用
  2. async-awaitを使用する場合
  3. async.jsライブラリの使用

Async.jsライブラリの使用

async.js ライブラリを使用してコールバック地獄を回避する方法について説明します。

async.js の公式 Web サイトの説明によると、Async は非同期 JavaScript を使用するための直接的で強力な機能を提供するツール モジュールです。

Async.js は合計で約 70 個の関数を提供します。ここでは、async.waterfall() と async.series() の 2 つについてのみ説明します。

非同期ウォーターフォール()

この関数は、いくつかのタスクを次々に実行し、前のタスクの結果を次のタスクに渡す場合に便利です。これは、関数「tasks」の配列と、最終的な「callback」関数を取ります。この関数は、「tasks」配列内のすべての関数が完了した後、または「callback」がエラー オブジェクトとともに呼び出された後に呼び出されます。

var async = require('async');
非同期ウォーターフォール([
    関数(コールバック) {
      /*  
        ここで、最初の引数の値はnullであり、
        関数の配列から次の関数が実行されます。
        値がtrueまたは任意の文字列の場合、最終的なコールバック関数
        配列内の残りの関数は実行される 
        実行されません。
      */
        コールバック(null, 'one', 'two');
    },
    関数(パラメータ1、パラメータ2、コールバック) {
        // param1 は 'one' になり、param2 は 'two' になります
        コールバック(null, 'three');
    },
    関数(パラメータ1、コールバック) {
        // param1 は 'three' になります
        コールバック(null、'完了');
    }
]、関数(エラー、結果){
    /*
      これは最後のコールバック関数です。
      結果は「完了」になります
    */
});

非同期シリーズ()

関数を実行し、すべての関数が正常に実行された後に結果を取得する必要がある場合に便利です。 async.waterfall() と async.series() の主な違いは、async.series() ではある関数から別の関数にデータを渡さないことです。

非同期.シリーズ([
    関数(コールバック) {
        // 何かやってみる ...
        コールバック(null、'one');
    },
    関数(コールバック) {
        // さらにいくつかの作業を実行します...
        コールバック(null, 'two');
    }
]、
// オプションのコールバック
関数(エラー、結果) {
    // 結果は ['one', 'two'] に等しくなります
});

Javascript コールバックとクロージャ

閉鎖

技術的に言えば、クロージャとは、周囲の状態を参照する関数をまとめてグループ化したものです。

つまり、クロージャにより、内部関数から外部関数のスコープにアクセスできるようになります。

クロージャを使用するには、別の関数内に関数を定義する必要があります。その後、それを別の関数に返すか渡す必要があります。

コールバック

概念的には、コールバックはクロージャに似ています。コールバックは基本的に、関数を別の関数として使用することです。

最後の言葉

この記事により、JavaScript コールバック関数に関する疑問がすべて解消されることを願っています。この記事が役に立った場合は、他の人と共有してください。

JavaScript コールバック関数に関するこの記事はこれで終わりです。JavaScript コールバック関数に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript ドキュメント オブジェクト モデル DOM
  • JS の == と === の違いをご存知ですか?
  • JavaScript の未定義と null の相違例の分析
  • JavaScript es6 における var、let、const の違いの詳細な説明
  • Web面接におけるJS事前解析と変数プロモーションの違い
  • JavaScript におけるホイスティングの詳細説明(変数ホイスティングと関数宣言ホイスティング)
  • JavaScript でローカル変数をグローバル変数に変換する方法
  • JavaScript クロージャの説明
  • Javascriptの基礎を学ぶための10の重要な質問

<<:  Linux で圧縮ファイルの内容を表示する 10 の方法 (要約)

>>:  ウィンドウ環境で VScode を使用して仮想マシン MySQL に接続する方法

推薦する

JS関数の呼び出し、適用、バインドの超詳細な方法

目次JS 関数呼び出し、適用、バインドメソッド1. call() メソッド1. call() メソッ...

win2008R2 64 ビット システムでの mysql5.7.17 のインストールと構成の例

123WORDPRESS.COM では、さまざまな環境での MYSQL の他のバージョンのインストー...

JS正規RegExpオブジェクトについての簡単な説明

目次1. RegExpオブジェクト2. 文法2.1 定義2.2 修飾子2.3 角括弧2.4 メタ文字...

MySQL でテーブルスペースの断片化を解消する詳細な例

MySQL でテーブルスペースの断片化を解消する詳細な例断片化の原因(1)テーブルのストレージは断片...

LinuxでIPアドレスが表示されない問題の解決方法

目次序文解決:ステップ1ステップ2序文環境: VMware Workstation 上に Linux...

簡潔なReactコンポーネントを書くためのヒント

目次スプレッド演算子を使用してプロパティを渡すのは避けてください関数パラメータをオブジェクトにカプセ...

Linux で gdb を使用してコア ファイルをデバッグする方法

1.コアファイルプログラム実行中にセグメンテーション エラー (コア ダンプ) が発生すると、プログ...

ウェブデザインとは何か

<br />元の記事: http://www.alistapart.com/articl...

MySQL 5.6 のインストール手順(画像とテキスト付き)

MySQL はオープンソースの小規模リレーショナル データベース管理システムです。現在、MySQL...

Linux で lvm 論理ボリューム パーティションのサイズを調整するチュートリアル (xfs や ext4 などのさまざまなファイル システム用)

序文システムをインストールしたときに、パーティション領域を適切に割り当てませんでした。その後のメンテ...

Linux は、ディレクトリが存在するかどうかを判断するために if を使用します。

Linux で if を使用してディレクトリが存在するかどうかを判断する方法方法は次のとおりです。...

ドラッグ効果を実現するための js オブジェクト指向メソッド

この記事では、ドラッグアンドドロップをJSオブジェクト指向で実装するための具体的なコードを参考までに...

HTML の長いテキストは、タグの幅を超えると自動的に切り捨てられます。

長いテキストを表示する場合、C# 側で文字をインターセプトする必要があることがよくありますが、長いテ...