JavaScript非同期問題解決方案

4677 ワード

1.はじめに
JavaScriptは単一スレッド言語であり、一般的なAjaxリクエストなどの比較的時間のかかる操作を実行する場合、後続のコードの実行をブロックしないために、非同期操作を実行する必要があることが多い.JSの運行メカニズムについて、みんなはチェンの1峰のこの文章を見ることができます:JavaScriptの運行メカニズムの詳しい解:更にEvent Loopについて話します
非同期操作をどのように処理するかはずっと注目すべき問題であり,この文章ではいくつかの一般的な非同期関数を処理する解決策を紹介する.
2.コールバック関数の使用
JQueryを使用したことがある場合は、コールバック関数は、マスター関数の実行後に実行される変数として別の関数に渡される関数であることをよく知っているに違いありません.
let delayWithCallback = (time, callback) => {
    console.log('handle...')
    setTimeout(() => {
        if (typeof callback === 'function') {
            callback(`success`)
        }
    }, time)
}

コールバックメソッドでコールバックを処理する
3.Promiseの使用
Promiseコンストラクション関数は、非同期操作が成功した後のコールバック関数と非同期操作が失敗した後のコールバック関数を表す2つのパラメータをパラメータとして受け入れる.ここでいう「成功」と「失敗」は主に理解しやすいためであり、正確にはresolveとrejectがPromiseの状態を変え、resolveがPromiseの状態をresolved、rejectがPromiseの状態をrejectedとする
let index = 1;
let delayWithPromise = (time) => {
    return new Promise((resolve, reject) => {
        console.log(`task${index} handle...`)
        index++
        setTimeout(() => {
            resolve('success')
        }, time)
    })
}

3.1 Promiseインスタンスでthenメソッドを使用してコールバックを処理するthenメソッドはPromiseプロトタイプ上のメソッド、Promise.prototype.then(),thenメソッドは2つのパラメータを受け入れ,第1のパラメータはResolved状態のコールバック関数,第2のパラメータ(オプション)はRejected状態のコールバック関数である
let func2 = () => {
    console.log('start')
    delayWithPromise(1000).then(result => {
        console.log(result)
        console.log('end')
    })
} 
func2()

3.2複数の非同期操作
学生情報を取得するための次の非同期方法があると仮定する
let getJSON = (key) => {
    const data = {
        stu: 'stu1',
        stu1: {
            age: 1
        },
        stu2: {
            age: 2
        }
    }
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve(data[key])
        }, 100);
    })
}

Promise.allメソッドは、複数のPromiseインスタンスを新しいPromiseインスタンスにパッケージするために使用されます.この場合、複数のPromiseインスタンスは同期して実行できます.戻り値は、各操作の結果を含む配列です.
let func3 = () => {
    Promise.all([
        getJSON('stu1'),
        getJSON('stu2')
    ]).then(stu => {
        console.log(stu)
    })
}
func3()

3.3チェーンコールPromise非同期ネスト問題の解決
上のコードでは、操作の実行順序には関心がありません.Promise.allメソッドは、複数の非同期操作が同時に実行される問題をよく解決することができる.現在の非同期操作が前の操作の結果に依存するとfunc 4()のようなコードが書きやすくなり、func 4()ではネストされた階層がまだ少ない(2層)、20、30層あるとコードのメンテナンスが難しくなります
let func4 = () => {
    getJSON('stu').then(result1 => {
        getJSON(result1).then(result2 => {
            .....
        })
    })
}
func4()

thenメソッドで新しいPromiseインスタンスを返すと,チェーン呼び出し関係を形成することができる.
thenチェーン呼び出しを採用すると、非同期関数間の階層的なネストを回避し、元の非同期関数の「ネスト関係」を読みやすく理解しやすい「チェーン」ステップ関係に変換し、func 5()のように順番に呼び出されるコールバック関数のセットを指定することができ、コードの構造が明確になります.
let func5 = () => {
    getJSON('stu')
        .then(result1 => {
            return getJSON(result1)
        }).then(result2 => {
            console.log(result2)
        })
}
func5()

4.async/awaitによる非同期問題の解決
async/awaitはES 7の新しい特性です.以下はasyncに関するいくつかのポイントです.
  • functionの前にasyncキーワードを追加すると、これはasync関数
  • であることを示します.
  • asyncの戻り値はPromiseオブジェクトで、thenメソッドでコールバック関数
  • を追加できます.
  • awaitの後に続くのはpromiseオブジェクトであるべきであり、そうでなければ直ちにresolveのPromiseオブジェクト
  • に変換される.
  • awaitは、promiseが結果を返すのをここで待ってから実行を続行することを示します.
  • let func6 = async () => {
        console.log('start')
        let result = await delayWithPromise(1000);
        console.log(result)
        console.log('end')
    }
    func6()
    

    4.1 async/await複数の非同期問題の処理
    一つ一つ実行
    let func7 = async () => {
        console.log('start')
        let result1 = await delayWithPromise(500)
        let result2 = await delayWithPromise(500)
        console.dir(result1, result2)
        console.log('end')
    }
    func7()
    

    同時実行
    let func8 = async () => {
        console.log('start')
        let [result1, result2] = await Promise.all([
            delayWithPromise(500),
            delayWithPromise(500)
        ])
        console.dir(result1, result2)
    }
    func8()
    

    func 7()とfunc 8()は処理時に若干異なりますが、func 7()ではtask1 handle...500 msを先に印刷してからtask2 handle...を印刷します
    しかしfunc 8()では、task1 handle...task2 handle...が同時に印刷され、func 7()タスクでは、func 8()が同時に同期して実行されるシーケンスブロック実行であることを示している.
    まとめると、複数の非同期操作を含む方法では、現在の操作が前の操作に依存した結果など、コードロジックに相互依存関係がある場合はfunc 7()という書き方を使用できます.
    非同期操作の間に依存関係がない場合はfunc 8()という書き方を使うべきです.これにより、前のawaitが後の異操作をブロックすることなく、すべての操作が同時に効率を大幅に向上させることができます.