async/await 깊이 이해

23790 단어
어떤 명칭이든 의미가 있으니, 먼저 글자의 뜻으로 이해해라.async는'비동기'의 약자이고,await는'asyncwait'의 약자라고 할 수 있다.그래서 잘 이해해야 한다. async는 하나의 기능이 비동기적이라는 것을 설명하고, await는 하나의 비동기적인 기능이 실행되기를 기다린다.
또한 재미있는 문법 규정이 하나 있다. await는 async 함수에만 나타날 수 있다. 그러면 작은 문제가 하나 있다. 만약에 await가 async 함수에만 나타날 수 있다면 async 함수는 어떻게 호출해야 하는가.
이 문제의 관건은 async 함수가 반환값을 어떻게 처리하는가에 있다.우리는 당연히 그것이 Return을 통해 우리가 원하는 값으로 되돌아오기를 희망하지만, 이렇게 하면 await가 아무 일도 없는 것 같다.그래서 코드를 써서 도대체 무엇을 되돌려주는지 보면 다음과 같다.
async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);
출력을 보고 문득 깨달은 것은 프로미스 대상이다.
c:\var\test> node --harmony_async_await .
Promise { 'hello async' }
그래서 async는 프로미스 대상을 되돌려줍니다. async 함수는 (함수 문장, 함수 표현식, Lambda 표현식) 프로미스 대상을 되돌려줍니다. 함수에서 직접 양을 되돌려주면 async는 이 직접 양을 프로미스를 통과합니다.resolve()는 Promise 객체로 봉인됩니다.그래서 더 이상 가장 바깥쪽으로await로 반환값을 얻을 수 없는 상황에서 우리는 원래의 방식을 사용해야 한다.then () 체인을 사용하여 Promise 객체를 처리합니다.이렇게
testAsync().then(v => {
    console.log(v);    //   hello async
});
    
지금 돌이켜서 생각해 보세요. 만약async 함수가 되돌아오는 값이 없다면 어떻게 해야 합니까?그것은 돌아올 것이라고 생각하기 쉽다 Promise.resolve(undefined).
Promise의 특징인 기다림이 없기 때문에 await 없이 async 함수를 실행하면 바로 실행하고 Promise 대상을 되돌려주며 뒤의 문장을 막지 않습니다.이것은 Promise 객체를 반환하는 일반적인 함수와 다름없습니다.
그럼 다음 관건은await 키워드입니다.

await 도대체 뭘 기다리고 있는 거야?


일반적으로 await는 async 함수의 실행을 기다리고 있습니다.그러나 문법에 의하면 await가 기다리는 것은 하나의 표현식이다. 이 표현식의 계산 결과는 Promise 대상이나 다른 값이다.async가 Promise 대상을 되돌려주기 때문에 awiat는 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 대상resolve를 기다린 다음에resolve의 값을 얻어서 await 표현식의 연산 결과로 삼을 것이다.위의 막힌 단어를 보고 당황했지... 안심해. 이것이 바로await가 async 함수에 사용해야 하는 이유야.async 함수 호출은 막히지 않습니다. 내부의 모든 막힘은 Promise 대상에 봉인되어 비동기적으로 실행됩니다.

async/await가 뭘 도와줬어요.


간단한 비교를 하다

    
위에서 설명한 바와 같이 async는 그 후의 함수 (함수 표현식 또는 Lambda) 의 반환 값을 Promise 대상으로 봉인하고, await는 이 Promise가 완성되기를 기다리며, 이resolve의 결과를 되돌려줍니다.
지금 예를 들면, 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();

눈앞의 학우들은 이미 takeLongTime()라고 명시하지 않은 것을 발견했다.실제로 async 자체가 되돌아오는 Promise 대상이다. 가불가takeLongTime() 결과는 똑같다. 만약 이해하지 못한다면 고개를 돌려 위의'async가 어떤 작용을 하는지 보십시오.
또 하나의 의문이 생겼다. 이 두 단락의 코드는 두 가지 방식이 비동기적으로 호출되는 처리(실제는 Promise 대상에 대한 처리)에 대한 차이가 뚜렷하지 않다. 심지어async/await를 사용하려면 코드를 많이 써야 한다. 그 장점은 도대체 어디에 있는가?
async/await의 장점은then체인을 처리하는 데 있다
단일한 Promise 체인은 async/await의 장점을 발견할 수 없지만 여러 개의 Promise로 구성된 then 체인을 처리해야 할 때 장점을 나타낼 수 있다(흥미롭다. Promise는 then 체인을 통해 다중 리셋 문제를 해결하고 지금은 async/await로 더욱 최적화한다).
하나의 업무를 가정하고 여러 단계로 나누어 완성하면 모든 단계는 비동기적이며 이전 단계의 결과에 의존한다.우리는 여전히 async 를 사용하여 비동기식 작업을 시뮬레이션합니다.
/**
 *   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

출력 결과setTimeoutresult의 매개 변수step3() = 700 + 200입니다.900 순서로 세 단계를 수행했는데 모두 doIt()밀리초를 사용했고 300 + 500 + 700 = 1500 계산 결과와 일치했다.
만약 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 구현과 같지만 이 코드는 훨씬 뚜렷해 보이고 거의 동기화 코드와 같다

그리고 더 멋있는 거.


지금 업무 요구를 바꾸면 여전히 세 가지 절차이지만, 모든 절차는 이전의 모든 절차의 결과가 필요하다.
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();

좀 복잡한 거 같지 않아요?그 한 무더기의 매개 변수 처리는 바로 Promise 방안의 사혈이다. 매개 변수 전달이 너무 번거로워서 보기만 해도 어지럽다.

좋은 웹페이지 즐겨찾기