Unit3 - [JS/Node] 비동기_1

예진·2022년 9월 26일
0

🔥 동기 & 비동기

- 동기(synchronous)

: 요청에 대한 결과가 동시에 일어난다. 특정 코드의 실행이 완료될 때까지 기다리고 난 후 다음 코드를 수행
( 작업은 순차적으로 실행되며 어떤 작업이 수행 중이면 다음 작업은 대기한다. )

- 비동기(asynchronous)

: 요청에 대한 결과가 동시에 일어나지 않는다. 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드들을 수행
( 작업이 종료되지 않은 상태라도 대기하지 않고 다음 작업을 수행한다. )

- 타이머 관련 API

setTimeout(callback, millisecond) : 일정 시간 후에 함수를 실행

  • 매개변수(parameter) : 실행할 콜백 함수, 콜백 함수 실행 전 기다려야 할 시간(밀리초)
  • return 값 : 임의의 타이머 ID

clearTimeout(timerId) : setTimeout 타이머 종료

  • 매개변수(parameter) : 타이머 ID
  • return 값 : 없음

setInterval(callback, millisecond) : 일정 시간의 간격을 가지고 함수를 반복적으로 실행

  • 매개변수(parameter) : 실행할 콜백 함수, 반복적으로 함수를 실행시키기 위한 시간 간격(밀리초)
  • return 값 : 임의의 타이머 ID

clearInterval(timerId) : setInterval 타이머를 종료

  • 매개변수 : 타이머 ID
  • return 값 : 없음
    (

1. 비동기 JavaScript

- blocking : 다른 함수를 호출할 때 제어권도 함께 넘겨주고, 작업이 끝난 후에 돌려받는 방식

- non-blocking : 다른 함수를 호출할 때 제어권을 넘기지 않고, 자신이 갖고 작업하는 방식

2. Callback

: 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
(함수를 명시적으로 호출하는 방식이 아닌 특정 이벤트가 발생했을 때 시스템에 의해 호출된다.)

  • 비동기 처리를 위한 하나의 패턴으로 콜백 함수 사용한다.
  • callback 함수를 통해 비동기 코드의 순서 제어 가능 = 비동기를 동기화 할 수 있다.

콜백 지옥(Callback Hell)

: 콜백 함수 호출이 중첩되어 복잡도가 높아지는 현상

function doStep1(init, callback) {
  const result = init + 1;
  callback(result);
}

function doStep2(init, callback) {
  const result = init + 2;
  callback(result);
}

function doStep3(init, callback) {
  const result = init + 3;
  callback(result);
}

// 콜백 지옥
function doOperation() {
  doStep1(0, result1 => {
    doStep2(result1, result2 => {
      doStep3(result2, result3 => {
        console.log(`result: ${result3}`);
         // 이를 반복하다보면 코드가 피라미드 형태를 띈다.
      });
    });
  });

}

doOperation();

=> 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 자주 등장하는데,
이러한 방식은 코드의 가독성을 나쁘게 하며, 코드 수정이 어려워진다.

  • 이러한 Callback Hell()의 현상을 방지하기 위해 Promise 사용

3. Promise

비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데, 이 콜백 함수는 resolvereject 함수를 인수로 전달받는다. ( 비동기 처리 상태와 처리 결과를 관리하는 객체이다. )
Promise 객체가 생성되면 executor는 자동으로 실행되고 작성했던 코드들이 작동된다.
=> 코드가 정상적으로 실행되면 resolve 함수 호출 / 에러 발생했을 경우 reject 함수 호출

new Promise

Promise는 class 이기 때문에 new 키워드를 통해 Promise 객체를 생성한다.

// Promise 생성
const promise = new Promise((resolve, reject) => {
 // Promise 함수의 콜백 함수 내부에서 비동기 처리 수행
 if (/* 비동기 처리 성공 */) {
     resolve('result');
 } else { /* 비동기 처리 실패 */
   reject('failure reason');
 }
});

Promise 객체의 내부 프로퍼티

new Promise 가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖는다.
but, 직접 접근이 불가능하며 .then, .catch, .finally 메서드를 사용해 접근 가능하다.

- State
: 기본 상태는 pending(대기) / 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하면 fulfilled(이행)으로 변경, 에러가 발생하면 rejected(거부)

- Result
: 처음은 undefined / 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하여 resolve(value)가 호출되면 value, 에러가 발생하면 reject(error)가 호출되면 error

Promise states

  • pending (대기) : 비동기 처리가 아직 수행되지 않은 초기 상태, 기본 상태
  • fullfilled (이행) : 비동기 처리가 수행된 상태(성공) -> resolve 호출
  • rejected (거부) : 비동기 처리가 수행된 상태(실패) -> reject 호출

- resolve(value) : 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
- reject(error) : 에러 발생 시 에러 객체를 나타내는 error와 함께 호출

Promise 후속 처리 메서드

.then() : 비동기 처리 성공 & 실패

promise.then( 
  function(result) { /* 결과(result)를 다룬다 */ },
  function(error) { /* 에러(error)를 다룬다 */ }
);
  • 첫 번째 인수는 promise가 이행되었을 때 실행되는 함수, 실행 결과를 받는다.
  • 두 번째 인수는 promise가 거부되었을 때 실행되는 함수, 에러를 받는다.

( 작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달한다. )

.catch() : 예외
에러가 발생한 경우만 다루고 싶다면 첫 번째 인수로 null을 전달하면 된다.

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("에러 발생")), 1000);
});

//  .catch(f)는 promise.then(null, f)과 동일하게 작동
promise.catch(alert);  //  1초 뒤 "에러 발생" 출력 

executor에 작성했던 코드들이 에러가 발생했을 경우, reject 함수를 호출하고 .catch 메서드로 접근할 수 있다.

Finally : executor에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally 메서드로 접근 가능

let promise = new Promise(function(resolve, reject) {
  resolve("성공");
});

promise
.then(value => {
	console.log(value);
	// "성공"
})
.catch(error => {
	console.log(error);
})
.finally(() => {
	console.log("성공이든 실패든 작동!");
	// "성공이든 실패든 작동!"
})

Promise chaining

Promise chaining가 필요한 경우는 비동기 작업을 순차적으로 진행해야 하는 경우
Promise chaining이 가능한 이유는 .then, .catch, .finally 의 메서드들은 Promise를 리턴하기 때문에 .then 을 통해 연결할 수 있고, 에러가 발생할 경우 .catch로 처리하면 된다.

let promise = new Promise(function(reolve, reject) {
  resolve('성공');
  ...
});
  
promise
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .catch((error) => {
    console.log(error);
    return '실패';
  })
  .finally(() => {
    console.log('성공이든 실패든 작동!');
  });

Promise.all()

: 여러 개의 비동기 작업을 동시에 처리하고 싶을 때 사용 ( 인자로는 배열을 받는다. )

const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));

// 기존 Promise chaining
const result = [];
promiseOne()
  .then(value => {
    result.push(value);
    return promiseTwo();
  })
  .then(value => {
    result.push(value);
    return promiseThree();
  })
  .then(value => {
    result.push(value);
   console.log(result);  // ['1초', '2초', '3초']
  })

// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
  .then((value) => console.log(value))  // ['1초', '2초', '3초']
  .catch((err) => console.log(err));
  • Promise.all() 의 전달인자의 형태는 프로미스 객체의 배열이다.

  • Promise.all() 사용 시 then 메서드의 매개변수는 배열 형태로 전달된다.

  • Promise.all() 은 인자로 받는 배열에 있는 Promise 중 하나라도 에러가 발생하면 나머지 Promise의 state와 상관없이 즉시 종료된다.

4. Async/Await

async 함수

function 앞에 async를 붙이면 해당 함수는 promise를 반환한다.

// `async`는 function 앞에 위치한다.
async function f() {
  return 1;
}

f().then(alert);  // 1

await

async 함수 내에서만 await 키워드 사용
await를 만나면 promise가 처리될 때까지 함수 실행을 기다리고, 결과는 그 이후에 반환한다.
promise가 처리되는 것을 기다리는 동안 다른 일을 할 수 있어 CPU 리소스가 낭비되지 않는다.

// 함수 선언식
async function funcDeclarations() {
	await 작성하고자 하는 코드
	...
}

// 함수 표현식
const funcExpression = async function () {
	await 작성하고자 하는 코드
	...
}

// 화살표 함수
const ArrowFunc = async () => {
	await 작성하고자 하는 코드
	...
}
    

// `await`는 `async` 함수 안에서만 동작한다.
async function f() {
  
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("완료"), 1000)
  });
  
  // promise가 이행될 때까지 기다린다
  let result = await promise;
  
  alert(result);  // 1초 뒤 "완료" 출력
}

f();
profile
😊

0개의 댓글