[JS] 비동기 / Promise / async.await (연습문제)

2022. 1. 6. 18:14·Archive
반응형

 

 

     


     

     

     

     

     

    비동기 개념

     

     

     

    웹 브라우저?

     : 서버에서 받아온 HTML, CSS, JS를 실행시켜주는 프로그램이다.

       브라우저는 자바스크립트를 실행하는 과정이 있다. 

     

       브라우저는 C++이라는 언어로 코드가 짜져있는데, 브라우저는 실행해야할 자바스크립트 코드를 발견하면

       C++ 언어로 만들어둔 stack에 넣어서 돌린다.

     

      stack은 다 집어넣고 맨 윗줄부터 하나하나 실행시키는 공간이다.

      하지만, 서버로의 ajax 요청, eventlistener, setTImeout 이런 코드들은 코드를 처리하기까지 시간이 오래걸린다.

      그래서 이런코드들은 stack에 쌓아서 실행하지않고, Queue라는 곳에 집어넣고 기다렸다가

      stack이 비어있을 때 차례로 Queue에서 stack으로 집어넣어서 실행해준다.

     

      따라서, stack을 바쁘게 만들면 setTimeout 이런 코드들은 실행이 불가능하다.

     

     

     

     

     

    🤷🏻‍♀️  시간이 오래 걸리는 작업을 꼭 해야한다면 ?

     

      1. setTimeout을 이용한다.

         작업을 0초마다 쪼개서 실행하게 한다. 그러면 0초마다 Queue로 보내기때문에 그 사이사이 사용자의 

         eventlistener이런 코드가 실행가능하게된다. ( 0초로 실행해도 4ms로 동작한다. 최소시간이 4ms )

     

     

      2. web worker를 이용한다.

        다른 js파일을 이용해서 오래걸리는 작업을 따로 작동시키고 그게 완료되면 값을 가져오라고 명령한다.

    (메인 js 파일)
    var myWorker = new Worker('worker.js'); 
    
    w.onmessage = function(e){
      console.log(e.data) //이러면 1 나올듯
    };
    (worker.js 파일)
    
    var i = 0;
    postMessage(i + 1); //postMessage라는 특별한 함수가 있음

    이런식으로 셋팅하면 worker.js에서 작업완료 시 postMessage() 이렇게 실행하면 다른 파일로 완료된 결과값을 전달해줄 수 있다.

    이러면 stack이 바빠지지않는다.

     

     

     

     

     

     🤷🏻‍♀️ 동기식 처리?

        : 동기식 처리는 한번에 코드 한줄씩 차례로 실행되는 것이다.

          위에서 설명된 웹브라우저의 stack이라는 코드실행공간에서 자바스크립트는 동기적으로 처리된다.

     

          but, 비동기처리 (asynchronous)가되는 특수한 함수들 때문에 가끔 비동기적 실행이 된다.

     

     

     

    console.log(1);
    setTimeout(()=>{ console.log(2) }, 1000 };
    console.log(3);
    
    //출력
    1
    3
    2

     

      setTimeout()이라는 함수는 Queue로 들어가서 제쳐두고, 다른 코드부터 실행되므로 1-3-2순서로 출력된다.

    이런 처리방식을 비동기라고 한다. ( 오래걸리는 작업이 있으면 제껴두고 다른거부터 처리 )

     

    자바스크립트 언어자체는 동기식 처리가 되지만,

    Web API와 연관된 특수한 함수들을 쓰면 작업이 오래걸리 때 다른것부터 실행이 가능하다.

    ( Web API에 오래걸리는 작업들을 넣어놓고 빨리 처리되는 함수들 먼저 실행한다. )

     

     

     

     


    Callback함수

     

     

     

    🤷🏻‍♀️ 비동기상황에서 순차적으로 코드를 실행하고 싶다면?

        :  콜백함수를 활용한다. 

           콜백함수는 간단하게 함수안에 들어가는 함수를 전부 콜백함수라 생각할 수 있다.

     

     

    function 첫째함수 () {}
    
    function 둘째함수 () {}
    
    //순서대로 실행하고 싶을때 setTimeout같은 함수가 있으면 이런식으로 실행시 실패한다.
    첫째함수()
    둘째함수()
    
    //이럴때 콜백함수로 실행하면 순차적으로 실행이 가능하다.
    첫째함수(둘째함수)

     

     

    callback 함수

    function 첫째함수(둘째) {
     console.log(1);
     둘째();
    }
    
    function 둘째함수(){
     console.log(2);
    }
    
    첫째함수(둘째함수);

     

     

    callback 함수의 문제점

    - 순차적으로 실행하고싶은 코드가 여러개라면 코드가 지저분해진다.

       이러한 문제점을 개선한 것이 promise.

    첫째함수(function(){
      둘째함수(function() {
        셋째함수(function() {
        
        })
      })
    });

     

     

     

     

     

     


     

     

     

     

     

    Promise

     

     

     

    Promise

    : 자바스크립트의 새로운 기능이라기보다는 코드/함수 디자인 패턴이다.

     (동기를 비동기로 만들어주는 코드가 아니고, 디자인 패턴)

     

     

    let 프로미스 = new Promise(성공, 실패) {
      let 어려운 연산 = 1+1;
      //연산 후 실행하고 싶은 코드를 적는다. 성공() 적으면 .then에 있는, 실패() 적으면 .catch
      //성공()
      //실패()
    };
    
    프로미스
    .then(()=> {})    //성공시 실행되는 코드
    .catch(()=> {})   //실패시 실행되는 코드

    new Promise()로 생성된 변수를 콘솔창에 출력해보면 현재상태를 알 수 있다.

    [ 3가지 상태 ]

    성공/실패 판정 전에는 pending

    성공 후에는 resolved

    실패 후에는 rejected

     

     

     

     

    프로미스안에 콜백함수 실행

    성공 했을 때 then() 함수내의 코드를 실행

    실패했을 경우에는 catch( ) 함수내의 코드를 실행한다.  (callback함수와 다른점)

     

     

     

    콜백대신 쓰는이유?

    - 콜백보다 코드가 깔끔하다.

    - 성공/실패의 경우 각각 다른 코드가 실행가능하다.

     

     

     

    [예시]

    > setTimeout 예시

     1초후에 성공하는 promise - 성공시 특정코드 실행

    let 프로미스 = new Promise(function (성공, 실패) {
      setTimeout(() => {
        성공();
      }, 1000);
    });
    
    프로미스
      .then(() => {
        console.log("success");
      })
      .catch(() => {
        console.log("fail");
      });

     

     

     

     

     


     

     

     

     

     

    async / await

     

     

     

    async를 사용하면 promise object가 저절로 생긴다.

    ( async는 함수 앞에만 붙일 수 있다. )

    async function 더하기(){
      return 1 + 1 
    }
    
    //async를 붙였으니까 then 사용 가능
    //return 결과값 then 함수에서 파라미터로 넣어주면 사용가능
    더하기().then(function(결과){
      console.log(결과)
    });

     

     

     

    then 대신 await를 async function 안에서 쓸 수 있다.

    await를 붙여주는 함수가 다 끝날 때 까지 기다려준다.

    async function 더하기() {
      let 연산 = new Promise((성공, 실패) => {
        let 결과 = 1 + 1;
        성공(100);
      });
    
      let 결과 = await 연산    //연산 promise를 기다린다음에 완료되면 결과를 변수에 담아라.
      console.log(결과)
      
      //아래의 promise코드와 위의 await는 같은 역할을 한다.
      //promise.then (()=> {
      //console.log('결과')
    }
    더하기()

     

     

     

     

    await는 실패하면 에러가 나고 코드가 멈춘다.

    promise가 실패할 경우 코드실행을 멈추고 싶지 않으면 try catch를 사용한다.

    try{}안의 코드가 에러나고 멈출경우, 대신 catch{}내부의 코드를 실행한다.

    try { 이걸해보고 에러나면 } catch { 이걸 실행해 }

    async function 연산 () {
     let 프로미스 = new Promise ((성공, 실패)=> {
     	let 연산 = 1+1;
        성공(연산)
     });
     
     try {
     	let 결과 = await 프로미스;
        console.log(결과);
     } catch {
        console.log('실패')
     }
    }

     

     

     

     


     

     

     

     

     

    연습 문제 (비동기, promise, async/await)

     

     

    [기초 문제]

    문제 1 — Promise 기본 생성

    /**
     * 1초 뒤에 "done"을 반환하는 Promise를 생성하는 함수를 작성하세요.
     *
     * 예시:
     * await delay(); // 👉 "done" (1초 뒤)
     */
    function delay(): Promise<string> {
      // TODO
    }

    🔹 힌트: setTimeout을 Promise로 감싸기 (new Promise(resolve => ...))

     

     

    더보기
      const delay = (): Promise<string> => {
        return new Promise((resolve) =>
          setTimeout(() => {
            resolve("done");
            console.log("done");
          }, 1000)
        );
      };

     

    ⚙️ 문제 2 — then 체이닝

    /**
     * 아래 Promise 체인을 완성하세요.
     * 1초 뒤 "A"를 반환한 다음, 그 결과에 "B"를 이어붙여 최종 결과는 "AB"가 되게 하세요.
     */
    const result = new Promise((resolve) => {
      // TODO
    })
      // TODO: then 체인으로 "B"를 붙이기
    
    console.log(await result); // 👉 "AB"

    🔹 힌트: .then()은 이전 resolve 결과를 다음 단계로 넘긴다.

     

     

    더보기
      const result = new Promise((resolve) => {
        setTimeout(() => resolve("A"), 1000);
      }).then((res) => {
        return res + "B";
      });

     


     

     

    💣 문제 3 — 에러 처리

    /**
     * Promise가 실패하면("Error!") catch 블록에서 "Recovered"를 반환하게 하세요.
     */
    const result = new Promise((_, reject) => {
      // TODO: "Error!"로 reject
    })
      // TODO: .catch()로 에러를 잡고 "Recovered" 반환
    
    console.log(await result); // 👉 "Recovered"

    🔹 힌트: .catch()도 Promise를 반환한다. 내부에서 return하면 “복구”됨.

     

     

    더보기
    const result = new Promise((_, reject) => {
      reject("Error!"); // ❌ 실패 신호를 보냄
    })
      .catch((err) => {
        console.log("에러 잡힘:", err);
        return "Recovered"; // ✅ 에러 대신 복구된 값 반환
      });
    
    console.log(await result); // 👉 "Recovered"

     

    🔄 문제 4 — async/await 변환

    /**
     * then/catch 체인으로 작성된 아래 코드를 async/await 문법으로 변환하세요.
     */
    new Promise((resolve) => setTimeout(() => resolve("OK"), 500))
      .then((res) => console.log(res))
      .catch((err) => console.error(err));

     

     

    더보기
    const solution = async () => {
      try {
        const data = await new Promise((res) => setTimeout(() => res("OK"), 500));
        console.log(data);
      } catch (err) {
        console.error(err);
      }
    };

     

     

    🧠 문제 5 — 안전한 실행 (try-catch or then-catch)

    /**
     * Promise가 성공하면 [data, undefined],
     * 실패하면 [undefined, error]를 반환하는 safePromise 함수를 작성하세요.
     *
     * 예시:
     * await safePromise(Promise.resolve("A")); // 👉 ["A", undefined]
     * await safePromise(Promise.reject("X"));  // 👉 [undefined, "X"]
     */
    async function safePromise<T>(promise: Promise<T>): Promise<[T?, any?]> {
      // TODO
    }

    🔹 힌트: try/catch 또는 .then().catch() 둘 다 가능!

     

    더보기
    // try ~catch
    async function safePromise<T>(promise: Promise<T>): Promise<[T?, any?]> {
      try {
        const data = await promise;     // ✅ await로 결과를 기다림
        return [data, undefined];       // 성공 시
      } catch (error) {
        return [undefined, error];      // 실패 시
      }
    }
    
    // then ~catch
    function safePromise<T>(promise: Promise<T>): Promise<[T?, any?]> {
      return promise
        .then((data) => [data, undefined] as [T, undefined])
        .catch((error) => [undefined, error] as [undefined, any]);
    }

     

     

    [응용 문제]

     

    🧩 문제 1 — 안전한 다중 요청 (Safe All)

    /**
     * 여러 Promise를 동시에 실행하고,
     * 성공한 결과만 모아서 반환하세요.
     * 실패한 Promise는 무시합니다.
     *
     * 예시:
     * await safeAll([
     *   Promise.resolve(1),
     *   Promise.reject("fail"),
     *   Promise.resolve(3)
     * ]);
     * // 👉 [1, 3]
     */
    async function safeAll(promises: Promise<any>[]): Promise<any[]> {
      // TODO
    }

     

    🔹 힌트: Promise.allSettled() 또는 .catch() 체이닝으로 실패 무시

     

    더보기
    async function safeAll(promises: Promise<any>[]): Promise<any[]> {
      const results = await Promise.allSettled(promises);
      return results
        .filter((r) => r.status === "fulfilled")
        .map((r) => (r as PromiseFulfilledResult<any>).value);
    }

     


     

    ⚙️ 문제 2 — 요청 타임아웃 처리

    /**
     * 주어진 Promise가 timeout(ms) 내에 완료되지 않으면 "timeout"을 반환하세요.
     *
     * 예시:
     * await withTimeout(fetchData(), 1000);
     * // fetchData()가 1초 이내에 완료되면 결과값 반환
     * // 1초를 초과하면 "timeout" 반환
     */
    async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | "timeout"> {
      // TODO
    }

    🔹 힌트: Promise.race()를 써야 한다. 하나는 원래 promise, 하나는 setTimeout으로 만들어진 “타이머 Promise”

     

    더보기
    async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | "timeout"> {
      const timeoutPromise = new Promise<"timeout">((resolve) =>
        setTimeout(() => resolve("timeout"), ms)
      );
      return Promise.race([promise, timeoutPromise]);
    }

     

    ⚡ 문제 3 — 순차적 비동기 실행 (Order 유지)

    /**
     * 비동기 작업 배열을 순서대로 실행하고,
     * 각 결과를 순서대로 배열로 반환하세요.
     *
     * 예시:
     * const tasks = [
     *   () => Promise.resolve("A"),
     *   () => new Promise(res => setTimeout(() => res("B"), 500)),
     *   () => Promise.resolve("C"),
     * ];
     * await runSequential(tasks);
     * // 👉 ["A", "B", "C"]
     */
    async function runSequential(tasks: (() => Promise<any>)[]): Promise<any[]> {
      // TODO
    }

    🔹 힌트: for...of 문이나 reduce로 순서 보장

     

     

    더보기
    async function runSequential(tasks: (() => Promise<any>)[]): Promise<any[]> {
      const results = [];
      
      for (const task of tasks) {
        const result = await task(); 
        results.push(result);
      }
      
      return results;
    }

     


     

    🧠 문제 4 — 실패 시 재시도 로직

    /**
     * 주어진 비동기 함수를 최대 retries 번 재시도하세요.
     * 모든 시도가 실패하면 마지막 에러를 throw하세요.
     *
     * 예시:
     * let count = 0;
     * await retry(async () => {
     *   count++;
     *   if (count < 3) throw "fail";
     *   return "success";
     * }, 5);
     * // 👉 "success"
     */
    async function retry<T>(fn: () => Promise<T>, retries: number): Promise<T> {
      // TODO
    }

    🔹 힌트: try/catch 안에서 재귀 호출 or while loop로 재시도

     

    더보기
      async function retry<T>(fn: () => Promise<T>, retries: number): Promise<T> {
        let count = 0;
        let lastError;
    
        while (count < retries) {
          try {
            return await fn();
          } catch (err) {
            lastError = err;
            count++;
          }
        }
    
        throw lastError;
      }

     


     

    💣 Promise 기반 큐 구현 

    /**
     * 한 번에 최대 N개만 동시에 Promise를 실행하는
     * 병렬 제어 함수 runLimited를 구현하세요.
     *
     * 예시:
     * const tasks = [
     *   () => delay(1000).then(() => 1),
     *   () => delay(500).then(() => 2),
     *   () => delay(200).then(() => 3),
     *   () => delay(100).then(() => 4),
     * ];
     * await runLimited(tasks, 2);
     * // 👉 [1, 2, 3, 4]
     */
    async function runLimited(tasks: (() => Promise<any>)[], limit: number): Promise<any[]> {
      // TODO
    }

    🔹 힌트: Promise.race()로 “먼저 끝난 작업”을 감지하고 다음 작업을 채워 넣는다.

    (file upload, API rate-limit에 자주 쓰임)

     

    더보기
    async function runLimited(tasks: (() => Promise<any>)[], limit: number): Promise<any[]> {
      const results: any[] = [];      // ✅ 최종 결과를 "원래 순서"대로 담을 배열
      let i = 0;                      // ✅ 다음에 실행할 task의 인덱스 포인터
    
      const running: Promise<void>[] = []; // ✅ 현재 실행 중인 작업(슬롯)들
    
      // ✅ "슬롯 하나"를 담당하는 실행 함수: 끝나면 스스로 다음 작업을 이어받음
      const runNext = async () => {
        if (i >= tasks.length) return;     // 1) 더 이상 실행할 작업이 없으면 종료
    
        const index = i++;                 // 2) 지금 이 슬롯이 맡을 작업 인덱스 할당(그리고 포인터 1 증가)
        try {
          const value = await tasks[index](); // 3) 해당 작업 실행(함수를 호출해야 Promise가 생김)
          results[index] = value;             // 4) 결과를 "원래 인덱스" 자리에 저장(순서 보장)
        } finally {
          // 5) 이 슬롯이 방금 맡은 작업을 끝냈으니, 남은 작업이 있으면 "다음 작업"을 이어서 수행
          await runNext();
        }
      };
    
      // ✅ 최초에 limit(또는 tasks 길이) 만큼 "슬롯"을 켠다
      for (let j = 0; j < Math.min(limit, tasks.length); j++) {
        running.push(runNext());  // 각 슬롯은 runNext 루프를 통해 작업을 연속적으로 이어받음
      }
    
      // ✅ 모든 슬롯이 자기 몫의 작업을 다 끝낼 때까지 대기
      await Promise.all(running);
    
      return results;             // ✅ 순서 보장된 최종 결과
    }

     

    반응형
    저작자표시 비영리 변경금지 (새창열림)

    'Archive' 카테고리의 다른 글

    [TS] declare / d.ts / index signatures  (0) 2022.01.13
    [TS] React  (0) 2022.01.12
    [GIT] Github 잔디 안심어지는 현상  (0) 2022.01.05
    [JS] getter,setter / import,export  (0) 2022.01.05
    [TS] Private / Static / Generic 등  (0) 2022.01.05
    'Archive' 카테고리의 다른 글
    • [TS] declare / d.ts / index signatures
    • [TS] React
    • [GIT] Github 잔디 안심어지는 현상
    • [JS] getter,setter / import,export
    manon_e
    manon_e
    • manon_e
      개발 블로그
      manon_e
    • 전체
      오늘
      어제
      • 💻 (75)
        • Frontend (9)
          • React | Next.js (6)
          • Memo (3)
        • CS (8)
          • 네트워크 (0)
          • 자료구조 + 알고리즘 (5)
          • 컴퓨터 구조 + 운영체제 (3)
        • Cloud & Infra (2)
        • Project (4)
        • Archive (52)
    • 인기 글

    • 태그

      티스토리챌린지
      use cache
      정규표현식
      ES6
      staticGeneration
      react hook form
      Testcode
      가상DOM
      수량자
      Next.js
      오블완
      getstaticprops
      pre-rendering
      REACT
      supabase
      life-cycle
      getStaticPaths
      git
      typeScript
      package_manager
    • hELLO· Designed By정상우.v4.10.3
    manon_e
    [JS] 비동기 / Promise / async.await (연습문제)
    상단으로

    티스토리툴바