JavaScriptのasync/await

6886 ワード

原文のリンク:https://segmentfault.com/a/1190000007535316?utm_source=tag-newest
asyncとawaitは何をしていますか?
いずれの名前も意味があります.まずは字面の意味から分かります.asyncは「非同期」の略で、awaitはasync waitの略と考えられます.だからasynctionは非同期であるということをよく理解しておくべきです.awaitは非同期的な方法の完成を待つために使われます.
もう一つ面白い文法の規定があります.awaitはasync関数にしか現れません.そして、注意深い友達から質問があります.awaitがasync関数の中にしか現れないなら、このasync関数はどうやって呼びますか?
awaitでasync関数を呼び出す必要があるなら、この呼び出しの外はasync関数をもう一つ包装しなければなりません.そして……死のサイクルに入ります.いつまでたってもいい日はありません.
async関数がawaitで呼び出す必要がないとしたら、asyncはいったいどんな役割を果たしますか?
asyncはどんな役割をしますか?
この問題の鍵はasync関数はどうやってその戻り値を処理しますか?
もちろん、私たちはreturn文を通じて直接に私たちの欲しい値を返してほしいですが、もしそうなら、awaitということはないようです.だから、コードを書いてみて、何を返しますか?
async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);
アウトプットを見て、はっと悟りました.アウトプットはPromiseの対象です.
c:\var\test> node --harmony_async_await .
Promise { 'hello async' }
したがって、async関数はPromiseオブジェクトを返します.この情報は文書からも入手できます.async関数(関数文、関数式、Lamda式を含む)はPromiseオブジェクトに戻ります.関数の中でreturnの直接量があれば、asyncはこの直接量をPromise.resove()を通じてPromiseオブジェクトにパッケージします.
async関数が戻ってきたのはPromiseオブジェクトですので、最外階でawaitでその戻り値を取得できない場合、もちろん元の方式でこのPromiseオブジェクトをthen()チェーンで処理します.このように.
> testAsync().then(v => {
    console.log(v);    //    hello async
});
今から振り返れば、async関数が戻り値を示さなかったら、どうなりますか?簡単に考えられます.Promise.resoveに戻ります.
Promiseの特徴を連想します.待ち時間がないので、awaitがない場合はasync関数を実行します.すぐ実行して、Promiseオブジェクトに戻ります.そして、後の文をブロックすることはありません.これは普通にPromiseオブジェクトに戻る関数と同じです.
次のポイントはawaitキーワードにあります.
awaitは一体何を待っていますか?
一般に、awaitはasync関数の完成を待っていると考えられています.しかし、文法的にはawaitが待っているのは表現です.この表現の計算結果はPromiseオブジェクトまたは他の値です.
async関数はPromiseオブジェクトに戻るので、awaitはasync関数の戻り値を待つために使えます.これはawaitがasync関数を待っていると言ってもいいですが、それなどが実際の戻り値です.awaitはPromiseオブジェクトなどに限らず、任意の表現の結果を待つことができますので、awaitの後は実際に普通の関数に接続して呼び出したり、直接量を呼ぶことができます.したがって、以下の例は完全に正しく動作します.
function getSomething() {
    return "something";
}

async function testAsync() {
    return Promise.resolve("hello async");
}

async function test() {
    const v1 = await getSomething();
    const v2 = await testAsync();
    console.log(v1, v2);
}

test();
awaitは待ちます.それから.
awaitは待ちたいもの、Promiseの対象、または他の値を待っています.まず、awaitは演算子です.表現を構成するために、await表現の演算結果はそれなどのものによって決まります.
もしそれがPromiseオブジェクトでないなら、await表式の演算結果はそれが待っているものです.
もしそれがPromiseオブジェクトであるなら、awaitは忙しいです.後のコードをブロックし、Promiseオブジェクトresoveを待って、resoveの値を得て、await表現の演算結果とします.
上の渋滞という言葉を見て、慌てるでしょう.安心してください.これはawaitがasync関数に使わなければならない理由です.async関数の呼び出しは、ブロックを引き起こすことはありません.その内部のすべてのブロックは、Promiseオブジェクトに非同期的に実装されます.
async/awaitは何をしてくれましたか?
簡単な比較をする
以上では、asyncがその後の関数(関数式またはLambanda)の戻り値をPromiseオブジェクトにパッケージ化すると説明しましたが、awaitはこのPromiseが完了するのを待って、その結果を返します.
例えば、setTimeoutで時間がかかる非同期操作をシミュレートします.まず、async/awaitを使わないとどう書きますか?
function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

takeLongTime().then(v => {
    console.log("got", v);
});
async/awaitに変えたら、こうなります.
function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);
}

test();
目の先の学友はすでにtake LongTime()がasyncと申明していないことを発見しました.実は、take LongTime()自体は帰ってきたPromiseの対象です.asyncを入れないと結果は同じです.もし分からなかったら、振り返ってみてください.
もう一つの疑問が発生しました.この二つのコードは、非同期呼出の処理(実際にはPromiseオブジェクトに対する処理)の違いがあまり明らかではないです.async/awaitを使うには、コードを多く書く必要があります.その利点はどこにありますか?
async/awaitの優位はthenチェーンを処理することにあります.
単一のPromiseチェーンはasync/awaitの優位性を発見できませんでした.しかし、複数のPromiseからなるthenチェーンを処理する必要があれば、優位性が現れます.
一つのサービスを複数のステップに分けて完了すると仮定し、各ステップは非同期であり、前のステップの結果に依存する.非同期動作をシミュレーションするためにsetTimeoutを使用したままである.
/**
 *      n,           (  )
 *        n + 200,          
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}
今はPromise方式でこの三つのステップの処理を実現します.
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms
出力結果resultはStep 3()のパラメータ700+200=900です.doIt()は3つのステップを順次実行し、合計300+500+700=1500ミリ秒を使用し、consone.time()/soline.timeEnd()と計算した結果と一致した.
async/awaitで実現すれば、こうなります.
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();
結果は前のPromiseと同じですが、このコードはかなりはっきりしているように見えます.同期コードとほぼ同じです.
もっとかっこいいのがあります
今は業務要求を変更します.まだ3つのステップですが、各ステップは前の各ステップの結果が必要です.
function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}
今回はまずasync/awaitで書きます.
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms
実行時間が長くなったと感じる以外に、前の例と変わらないようです.焦らないでください.それをPromiseと書いたらどうなりますか?
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();