JavaScriptのコールバックのマスタリング


JavaScriptのコールバック、約束と非同期の待機は、JavaScriptの概念を理解する最も重要なハードの一つです.JavaScript開発者の理解これらの概念は重要であり、クールの中であなたを置くて😎 JavaScript開発者ので、これらの概念に注意を払う必要があります.
これと次の記事では、我々は簡単でわかりやすい方法でこれらの概念を説明しようとしているので、それをバックルは😁. ここではJavaScriptのコールバックを見ます.我々は、彼らが何であるか、なぜ、どこで我々がそれらを使用するかを調査するつもりです.また、コールバックの潜在的な問題が表示されます

コールバック


I'm going to assume you know exactly 0 about callbacks. If I'm assuming wrong, just scroll down a bit.


私が初めてJavaScriptを学んだとき、それは私が機械としての機能について考えるのを助けました.これらの機械はあなたが望むものを何でもできる.彼らは入力を受け入れて、値を返すことさえできます.各マシンには、マシンを実行するときに押すことができるボタンがあります.
function add(x, y) {
    return x + y;
}

add(2, 3); // Press the button, run the machine.
私はボタンを押すかどうか、ボタンを押すか、誰か他の人が問題ではないボタンを押します.ボタンが押されるときはいつでも、その機械は走りそうです.
function add(x, y) {
    return x + y;
}

const me = add;
const you = add;
const someoneElse = add;

me(2, 3); // Press the button, run the machine.
you(2, 3); // Press the button, run the machine.
someoneElse(2, 3); // Press the button, run the machine.
上のコードではadd 関数は3つの異なる変数me ,畝you , また、someoneElse . それは、オリジナルのadd 我々が作成した変数とそれぞれの変数はメモリ内の同じ場所を指している.彼らは文字通り異なった名前の下で全く同じことです.だから、私たちがme ,畝you , またはsomeoneElse , それは私たちがadd . さて、もし我々が我々の怒りを取るならadd マシンと他のマシンに渡す?注意してください、それが押されている場合は、ボタンを押すと問題ではありません.
function add(x, y) {
    return x + y;
}

function addFive(x, addReference) {
    return addReference(x, 5); // 15 - Press the button, run the machine.
}

addFive(10, add); // 15
あなたの脳は、この1つで少し変になったかもしれません、しかし、新しい何もここで起こっていません.代わりに"ボタンを押す"add , 私たちはadd 汝への口論addFive , 名前を変更するaddReference , そして、私たちは“ボタンを押して”またはそれを起動します.
JavaScript言語のいくつかの重要な概念を強調します.まず、関数に引数として文字列や数値を渡すことができますので、引数として関数への参照を渡すこともできます.これを行うとき、引数として通過している関数は、関数コールバック関数と呼ばれ、コールバック関数を渡している関数を関数と呼びます.
語彙が重要であるので、彼らがデモしている概念にマッチするために名前を変えられた変数と同じコードは、ここにあります.
function add(x, y) {
    return x + y;
}

function higherOrderFunction(x, callback) {
    return callback(x, 5);
}

higherOrderFunction(10, add);
このパターンはよく見るべきである.JavaScriptの配列メソッドを使ったことがあるなら、コールバックを使いました.あなたがlodashを使用したならば、あなたはコールバックを使いました.jQueryを使ったことがあるなら、コールバックを使いました.
[1, 2, 3].map((i) => i + 5);

_.filter([1, 2, 3, 4], (n) => n % 2 === 0);

$("#btn").on("click", () => console.log("Callbacks are everywhere"));
一般に、コールバックには2つの一般的なユースケースがあります.第一、そして私たちが見る.map ○○_.filter例として、一つの値を別の値に変換するのは良い抽象化です.私たちは“ヘイ、ここに配列と関数です.先に行くと私はあなたに与えた機能に基づいて新しい値を得る”.第二に、jQueryの例で見ると、特定の時間まで関数の実行を遅延させます.「ここでは、この関数があります.btn 角をクリックする.「特定の時間までの関数の実行を遅らせる」というのはこの2番目のユースケースです.
たった今、我々は同期している例を見ました.我々はこのポストの冒頭で話したように、我々が構築するアプリのほとんどは、彼らが前に必要なすべてのデータを持っていない.代わりに、ユーザーは、アプリケーションと対話するように外部データを取得する必要があります.我々は、コールバックがどのように大きな使用ケースでありえるかについて見ました
このため、再度、“特定の時間までの関数の実行を遅らせる”ことができます.それは私たちがどのように我々がデータ取得で働くためにその文を適応させることができるかについて見るのを想像しません.関数の実行を遅延させるのではなく、特定の時間に達するまで、関数の実行を遅らせることができます.こちらが
この最も人気のある例はjQueryのgetJSON 畝法
// updateUI and showError are irrelevant.
// Pretend they do what they sound like.
const id = "endalk200";

$.getJSON({
    url: `https://api.github.com/users/${id}`,
    success: updateUI,
    error: showError
});
我々はユーザーのデータがあるまで我々はアプリのUIを更新することはできません.それで、我々は何をしますか?ねえ、ここでオブジェクトです.
リクエストが成功し、先に行くとsuccess ユーザのデータを渡す.そうでなければ、先に行って、電話してくださいerrorエラーオブジェクトを渡す.あなたは、それぞれのメソッドが何をするかについて心配する必要はありません
これは、asyncリクエストのコールバックを使用するという完璧なデモンストレーションです.
この時点で、同期と非同期の両方のコードバックとどのように有益であるかを学びました.我々がまだ話していないことは、コールバックの暗い面です.以下のコードを見てください.何が起こっているかわかりますか.
// updateUI, showError, and getLocationURL are irrelevant.
// Pretend they do what they sound like.
const id = "endalk200";

$("#btn").on("click", () => {
    $.getJSON({
        url: `https://api.github.com/users/${id}`,
        success: (user) => {
            $.getJSON({
                url: getLocationURL(user.location.split(",")),
                success(weather) {
                    updateUI({ user, weather: weather.query.results });
                },
                error: showError
            });
        },
        error: showError
    });
});
注意してくださいコールバックのいくつかのより多くの層を追加しました.まず、最初のAjaxリクエストを、idの要素がある要素まで実行しないでくださいbtn 角をクリックする.ボタンをクリックすると、最初の要求を行います.そのリクエストが成功すると、2番目のリクエストを行います.そのリクエストが成功した場合、updateUI 両方のリクエストから得たデータを渡す方法.あなたが一目でコードを理解したかどうかに関係なく、客観的にそれははるかに前にコードよりも読みにくいです.これは「コールバック地獄」の話題に私たちをもたらします.
人間として、我々は自然に連続して考える.入れ子になったコールバックの中に入れ子にされたコールバックを持つとき、それはあなたの自然な考え方からあなたを強制します.バグが発生するときにどのようにあなたのソフトウェアが読み取られ、どのように自然に考えるの間に切断されます.
ソフトウェアの問題に対する大部分の解決策と同様に、「コールバック地獄」を消費しやすくするための一般的に規定されたアプローチは、あなたのコードをモジュール化することです.
const getUser = (id, onSuccess, onFailure) => {
    $.getJSON({
        url: `https://api.github.com/users/${id}`,
        success: onSuccess,
        error: onFailure
    });
};

const getWeather = (user, onSuccess, onFailure) => {
    $.getJSON({
        url: getLocationURL(user.location.split(",")),
        success: onSuccess,
        error: onFailure
    });
};

$("#btn").on("click", () => {
    getUser(
        "endalk200",
        (user) => {
            getWeather(
                user,
                (weather) => {
                    updateUI({ user, weather: weather.query.results });
                },
                showError
            );
        },
        showError
    );
});
OK、関数名は何が起こっているかを理解するのを助けますが、客観的に「より良い」ですか?それほど多くない.我々はコールバック地獄の読みやすさの問題にバンドの援助を入れている.我々が自然に連続的に考える問題はまだ存在しています、そして、余分の機能でさえ、ネストされたコールバックはその連続した方法から我々を壊します.
コールバックの次の問題はinversion of control . コールバックを書くとき、コールバックを与えているプログラムが責任を持っていると仮定しています.あなたは本質的にあなたのプログラムのコントロールを別のものに反転させている
プログラム.jqueryやlodash、あるいはバニラJavaScriptのようなライブラリを扱う場合、正しい引数で正しいコールバック関数が呼び出されると仮定するのは安全です.しかし、多くのサードパーティ製ライブラリでは、コールバック関数はインターフェイスのインターフェイスです.第三者の図書館は、もっともらしい.
かどうかを目的または誤って、どのようにコールバックとの対話を破る.
const criticalFunction = () => {
    // It's critical that this function
    // gets called and with the correct arguments.
};

thirdPartyLib(criticalFunction);
あなたが1つを呼び出していないのでcriticalFunction , あなたはいつ、何の引数を呼び出すと0のコントロールがあります.ほとんどの時、これは問題ではありませんが、それが大きいとき、それは大きいものです.
次の記事ではJavaScriptの約束を調査し、どのように潜在的な解決策を提供することができます
inversion of control 問題
あなたはJavaScriptの約束の次の記事を見つけることができますhere