Javascript _ promise

이승미·2022년 6월 2일
0

봐도 봐도 역대급으로 이해가 안가는 프로미스...!
블로그 정리를 하다보면 스스로 개념 정리가 어느정도 될 듯 해서 블로그 정리를 해본다!

일단 프로미스에 대해서 정리하기 전에 동기, 비동기에 대해서 부터 알아야 한다.


1. 동기와 비동기

동기 (synchronous) : 순차적, 물이 다 끓을 때까지 아무것도 하지 않으며 기다리기
비동기 (Asynchronous) : 비순차적, 물이 끓을 동안 코딩 공부하기

<동기적 코드>

const name = 'seungmi';
const age = 25;
const gender = 'female';

console.log(name); // 1. seungmi

function getAge (age) {
  return age; }

console.log(getAge(age)); //2. 25
console.log(gender); //3. female

동기적으로 코드를 작성하면 name, age, female 이 입력한 순서대로 출력된다.

<비동기적 코드>

const name = 'seungmi';
const age = 25;
const gender = 'female';

console.log(name); // 1. seungmi

setTimeout(function() {  // 3. 25 (2초 후 실행)
  console.log(age);  //setTimeout(실행할 코드 담고있는 함수, 밀리초단위)
}, 2000);  

console.log(gender); // 2. female

위와 달리 setTimeout() 함수를 사용해 비동기적으로 코드를 작성하면 setTimeout() 함수의 연산이 끝나기까지의 2초간의 시간을 기다리지 않고 바로 뒤의 'console.log(gender)'가 실행된다. 그래서 name, female, age 순서대로 출력된다.

// ... (다른 코드들)
const btn = document.querySelector('button');
btn.onclick = function() {  // 버튼이 클릭될 때만 함수가 실행됨.
  console.log('버튼이 클릭되었습니다');
}
// ... (다른 코드들)

또 다른 예시로는 이전에 돔에서 다뤘던 이벤트핸들러(이벤트리스너)가 있다. 위의 예시의 경우에는 순차적 코드 실행이 아닌, 버튼이 클릭 되었을 때만 해당 함수가 실행된다.

이렇게 비동기적으로 코드를 구현하면 특정 함수가 끝날 때까지 기다릴 필요 없이 다른 코드가 실행되므로 훨씬 효율적이다!


2. 비동기적 코드 구현하는 방법

그렇다면 비동기적 코드를 구현하는 방법에는 어떤 것들이 있을까? 이 방법에는 콜백 함수를 사용하는 방법, promise를 사용하는 방법, async&await를 사용하는 방법이 있다.

2-1. 콜백 함수 사용

우선, 위의 예시의 setTimeout() 함수의 경우처럼 콜백 함수를 사용하는 방법이 있다.

const getDataFromFile = function (filePath, callback) {
  fs.readFile(filePath, 'utf8', function(err,data) {
  if (err) {
    callback(err,null); //에러가 발생하면 콜백함수(function(err,data....))의 첫 번째 인자에 에러 객체가 전달
  } else {
    callback(null,data); // 그게 아닐 경우에는 콜백함수(function(err, data...))의 두 번째 인자에 파일 내용 전달
    }
  })
}

그러나 이 방법은 크게 추천되는 방법은 아니다. 콜백함수가 중첩될수록 코드의 가독성이 심하게 떨어지기 때문이다. (이를 콜백 지옥이라 부른다고 한다...!) 예시는 아래와 같다.

function hello() {
  setTimeout(function() {
    console.log('nice');
    setTimeout(function() {
      console.log('to');
      setTimeout(function() {
        console.log('meet');
        setTimeout(function() {
          console.log('you');
          },0);
       }, 0);
    }, 0);
 },0);
}

hello(); //nice to meet you

2-2. promise 사용

그래서 이런 콜백함수의 단점을 대체해주기 위해서 나타난 것이 promise 이다.

const getDataFromFilePromise = filePath => {
    return new Promise(function(resolve, reject) {
      fs.readFile(filePath, 'utf8', function(err,data) {
        if(err) reject(err);  //error가 일어날 경우에는 에러 객체를 promise의 두 번째 인자인 reject에 전달
        else resolve(data);  //그게 아닐 경우에는 파일의 내용을 promise의 첫 번째 인자인 resolve에 전달 
      })  
    })
};

위와 같이 promise의 기본 형태는 'new Promise(function(resolve, reject) ...'과 같다.
첫 번째 인자인 resolve는 promise 함수 내에서 호출할 수 있는 또 다른 함수로, 작업이 성공했을 때만 호출된다.
두 번째 인자인 reject는 promise 함수 내에서 호출할 수 있는 또 다른 함수로, 작업이 실패했을 때만 호출된다.

그리고 이 promise 작업이 성공, 혹은 실패하고 끝났을 때 각각의 다음 동작들을 지정해줄 수 있는데 그것이 바로 then 메서드, catch 메서드이다. 간단한 예제는 다음과 같다.

const p = new Promise(function(resolve, reject) {
    resolve();
});

p.then(function() {
    console.log('success');
});

p.catch(function(){
    console.log('error');
});

//success

2-3 async / await 사용

위의 promise를 좀 더 쉽게 사용할 수 있는 방법이 있다. 바로 async, await를 사용하는 방법이다.

const readAllUsersAsyncAwait = async () => { 
  // async 키워드는 함수를 선언할 때 붙여준다
  // promise 방식에서 new promise를 지우고 async를 붙여준다
let result1 = await getDataFromFilePromise(user1Path)
let result2 = await getDataFromFilePromise(user2Path)
// async와 await는 같이 나온다
// await -> 함수가 끝날때까지 기다려라! 
// await는 async 함수 내부에서만 동작한다.
return JSON.parse(`[${result1},${result2}]`);

new promise로 프로미스를 작성하지 않아도 함수 앞에 async를 붙이면 그 함수는 항상 프로미스를 반환하게 된다. 그러므로 async 키워드가 작성된 함수에는 then 키워드를 사용할 수 있다. async 키워드와 거의 같이 쓰이는 await 키워드는 async 함수가 끝날 때까지, 즉 프로미스가 끝날 때까지 기다리라는 의미이다.

다만 async/await를 사용할 때 주의할 점이 있다. 바로 async 키워드를 사용할 때는 resolve한 값만 내놓을 수 있다는 것이다. 그래서 await 키워드는 프로미스가 끝날 때까지 기다렸다가 resolve 한 값을 내놓게 된다.

0개의 댓글