본문 바로가기
Front-end/React

async와 await에 대해 이해하기 [Javascript][NFasT 프로젝트 회고]

by 지니어스팍 2023. 12. 13.

♥ 목차 ♥

    728x90
    728x90

    NFasT 프로젝트의 코드를 다시보는 중에 axios 통신을 하는 과정에서 쓰이는 async와 await를 어떻게 이해해야하는지 명확한 논리 구조가 이해하기 어려웠다. await라 하면 이 함수를 실행한 후 다음 순서의 함수를 실행하라는 것인데 비동기 함수를 왜 만들어야하며 그 속의 순서를 보장하는 await는 어떻게 사용해야하며 async 함수 외부의 함수들은 어떻게 이 순서에 기다려야하는 것인지 궁금했다. 그래서 async와 await에 대해 좀 더 명확히 이해하고자 사례를 공부해보기로 했다!

     

    async와 await

    async는 비동기를 의미한다. async는 function앞에 붙이고 그 함수는 promise 함수를 반환한다.
    await는 async가 붙여진 함수 안에서만 사용 가능하다. await는 반드시 이 동작을 수행하고 다음동작을 수행하라는 의미이다.

    여기서 .then은 의미가 같은데 then 전의 동작을 수행하고 그 뒤에 함수를 실행하라는 의미이다.

     

    그럼 코드에서 async 와 await는 어떤 순서대로 작동할지 여러가지 상황에서 실험을 하며 훈련해보았다.

     

    사례 1 

    // Function to simulate an asynchronous task
    function simulateAsyncTask(name, duration) {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(`${name} completed`);
                resolve();
            }, duration);
        });
    }
    
    // Asynchronous Function using multiple await
    async function testAsync() {
        console.log('Async: Starting Task 1');
        await simulateAsyncTask('Task 1', 2000); // Wait for 2 seconds
        console.log('Async: Task 1 Awaited');
    
        console.log('Async: Starting Task 2');
        await simulateAsyncTask('Task 2', 1000); // Wait for 1 second
        console.log('Async: Task 2 Awaited');
    
        console.log('Async: All Tasks Completed');
    }
    
    // Synchronous code
    console.log('Synchronous: Before async function call');
    testAsync();
    console.log('Synchronous: After async function call');

     

    사례1 결과

    Synchronous: Before async function call
    Async: Starting Task 1
    Synchronous: After async function call // async 내 await를 기다리는 동안 다음 함수가 실행됨
    Task 1 completed
    Async: Task 1 Awaited
    Async: Starting Task 2
    Task 2 completed
    Async: Task 2 Awaited
    Async: All Tasks Completed

     

    async 함수 내에서 await를 만나면 무조건 기다려야한다.

    이때 중요한건 async 함수 내에서는 기다리지만 밖에서는 다음 함수를 진행한다. 따라서 'Synchronous: After async function call'이 Task 1 completed 전에 나온다.

    Async: Starting Task 1
    Synchronous: After async function call
    Task 1 completed

     

    사례 2

    // Synchronous Function
    function syncFunction() {
        let sum = 0;
        for (let i = 0; i < 1000000; i++) {
            sum += i;
        }
        console.log(`Sync: The sum is ${sum}`);
    }
    
    // Asynchronous Function using async/await
    async function asyncFunction() {
        let sum = await new Promise((resolve, reject) => {
            let sum = 0;
            for (let i = 0; i < 1000000; i++) {
                sum += i;
            }
            setTimeout(() => resolve(sum), 1000); // Simulate async operation
        });
        console.log(`Async: The sum is ${sum}`);
    }
    
    console.log("Starting Sync Function");
    syncFunction();
    console.log("Starting Async Function");
    asyncFunction();
    console.log("End of Script");

     

    사례2 결과

    Starting Sync Function
    Sync: The sum is 499999500000
    Starting Async Function
    End of Script // 비동기의 await을 기다리는 동안 다음 함수 실행
    Async: The sum is 499999500000 // 비동기의 await 동작 실행

     

    사례3

    // Simulated asynchronous task
    function simulateAsyncTask(name, duration, shouldFail = false) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if (shouldFail) {
                    reject(`Async Task ${name} failed`);
                } else {
                    console.log(`Async Task ${name} completed`);
                    resolve(name);
                }
            }, duration);
        });
    }
    
    // Asynchronous functions with .then() and .catch()
    function asyncFunctionWithThen() {
        console.log('Starting Async Function with Then');
        simulateAsyncTask('Then1', 2000)
            .then(result => {
                console.log(`Result from ${result}`);
                return simulateAsyncTask('Then2', 1000);
            })
            .then(result => {
                console.log(`Result from ${result}`);
            })
            .catch(error => {
                console.error(`Caught an error: ${error}`);
            })
            .finally(() => {
                console.log('Async Function with Then completed');
            });
    }
    
    // More complex async/await function
    async function complexAsyncAwaitFunction() {
        try {
            console.log('Starting Complex Async/Await Function');
            const result = await simulateAsyncTask('Await1', 1500, true); // This task will fail
            console.log(`Result from ${result}`);
        } catch (error) {
            console.error(`Error in Async/Await Function: ${error}`);
        } finally {
            console.log('Complex Async/Await Function completed');
        }
    }
    
    // Main execution flow
    console.log('Script start');
    asyncFunctionWithThen();
    complexAsyncAwaitFunction();
    console.log('Script end');

     

    사례3 결과

    Script start
    Starting Async Function with Then
    Starting Complex Async/Await Function
    Script end
    Error in Async/Await Function: Async Task Await1 failed // 시간초가 빠른 async의 await부터 실행
    Complex Async/Await Function completed
    Async Task Then1 completed // 동일한 sync 함수 내에서는 await와 then 순서대로 진행
    Result from Then1
    Async Task Then2 completed
    Result from Then2
    Async Function with Then completed

     

     

    사례 4

    // Simulated asynchronous task
    function simulateAsyncTask(name, duration) {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log(`Async Task ${name} completed`);
                resolve(name);
            }, duration);
        });
    }
    
    // Asynchronous functions with nested calls
    async function complexAsyncFunction1() {
        console.log('Starting Complex Async Function 1');
        const result1 = await simulateAsyncTask('1a', 1500);
        if (result1 === '1a') {
            await simulateAsyncTask('1b', 500);
        }
        console.log('Complex Async Function 1 complete');
    }
    
    async function complexAsyncFunction2() {
        console.log('Starting Complex Async Function 2');
        const results = await Promise.all([
            simulateAsyncTask('2a', 2000),
            simulateAsyncTask('2b', 1000)
        ]);
        console.log(`Complex Async Function 2 complete with results: ${results.join(', ')}`);
    }
    
    // Synchronous functions
    function intensiveSyncFunction() {
        console.log('Intensive Sync Function start');
        let sum = 0;
        for (let i = 0; i < 1000000; i++) {
            sum += i;
        }
        console.log('Intensive Sync Function complete');
    }
    
    // Main execution flow
    console.log('Script start');
    intensiveSyncFunction();
    complexAsyncFunction1();
    complexAsyncFunction2();
    console.log('Script mid-way');
    intensiveSyncFunction();
    console.log('Script end');

     

    사례4 결과

    Script start
    Intensive Sync Function start
    Intensive Sync Function complete
    Starting Complex Async Function 1 // await 기다림
    Starting Complex Async Function 2 // await 기다림
    Script mid-way
    Intensive Sync Function start
    Intensive Sync Function complete
    Script end
    Async Task 2b completed // await 함수 중에 가장 시간초가 적은 순서대로 진행 ( 같은 await 속에서는 코드 순서에 상관없이 시간 짧은 순서대로 진행)
    Async Task 1a completed // 그 다음으로 짧은 await을 실행한다.
    Async Task 2a completed //다음 await가 0.5초 걸렸지만 이미 함수2에서 걸리는 2초 시간이 경과하였으니 다시 함수 2로 간다.
    Complex Async Function 2 complete with results: 2a, 2b // 실행순서가 바뀌었지만 출력은 코드 순서대로 출력한다
    Async Task 1b completed
    Complex Async Function 1 complete

     

    4가지 사례를 통해 async await의 동작 순서를 이해할 수 있다. 

    async 내에서 await를 만나면 반드시 해당 동작이 다 끝날 때 까지 동일 async 함수내 다음 동작을 할 수 없다.

    하지만 async 함수 외부에서는 기다리지 않고 바로 다음 함수를 실행한다.

    실행하고 나서 다시 await 함수를 실행할 때에는 만약 기다려야하는 함수가 여러개이면 적은 시간 순서대로 실행한다.

    만약 같은 await 함수내 여러 함수라면 이중에서는 코드의 순서와 상관없이 시간이 적은수 순서대로 실행한다.

    하지만 코드적으로는 순서를 보장한다( Complex Async Function 2 complete with results: 2a, 2b  여기서 알 수 있다.)

    그럼 이제 !! 다시 axios 통신 코드를 보면 

    return async () => {
        const url = `http://localhost:8080/api/login`;
        await axios
          .post(url, JSON.stringify(data), {
            headers: {
              "Content-Type": "application/json;charset=utf-8",
            },
          })
          .then((response) => {
            const { data } = response;
            console.log("RESPONSE DATA ", data);
          })
          .catch((error) => {
            console.log("ERROR", error);
          });
      };

     

    이 함수는 post 요청을 보내서 result 값으로 response 를 받아와서 data를 불러오는 것으로 볼 수 있다.

    이때 await와 .then을 통해 response 값이 서버로부터 받아오질 때 까지 기다리고 순차적으로 data값에 response 를 넣어주는 과정을 이해할 수 있다.

    728x90
    728x90