[C/F TIL] 25일차 - JS/NODE 비동기

mu-eng·2023년 5월 16일
1

TIL (in boost camp)

목록 보기
26/53
post-thumbnail

Code States
Front-end boost camp
Today
I
Learned

☀️ 5월 16일 25일차 수업 시작!


☀️ 동기와 비동기

카페에서 커피를 주문하려고 줄 서 있는 김코딩, 그 뒤에 서 있는 박해커. 김코딩이 주문을 마칠 때 까지 박해커는 주문을 할 수 없다면 이 경우를 블로킹(blocking)이라고 부름

  • 블로킹(blocking) : 하나의 작업이 끝날 때까지, 이어지는 작업을 "막는 것"

이 때, 김코딩의 커피 주문 완료 시점과 박해커의 커피 주문 시작 시점이 같다면?

  • 동기적(synchronous)이다. 라고 한다.

효율 적인 카페 운영을 위해 커피 주문이 블로킹 되지 않고 언제든 주문을 받을 수 있어 김코딩의 주문 완료 시점과 박해커의 주문 시작 시점이 같을 필요가 없을 경우

  • 비동기적(Asynchronous execution) 실행

자바스크립트 에서의

  • 동기(synchronous) : 특정 코드의 실행이 완료될 때까지 기다리고 난 후 다음 코드를 수행하는 것
  • 비동기(asynchronous) : 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 수행하는 것

JavaScript는

  • 싱글 스레드 기반으로 동작
  • 즉, 동기적으로 작동
  • 하지만 JavaScript가 작동하는 환경(런타임)에서 비동기 처리를 도와주기 때문에
    특별한 작업 없이 비동기 처리 가능

☀️ 비동기로 작동하는 코드를 제어하는 방법 - callback 함수

  • 비동기를 동기화할 수 있다
// 터미널에 `node index.js`를 입력하여 비동기 코드가 작동하는 순서를 확인해보세요.
const printString = (string, callback) => {
  setTimeout(function () {
    console.log(string);
    callback();
  }, Math.floor(Math.random() * 100) + 1);
};

const printAll = () => {
  printString('A', () => {
    printString('B', () => {
      printString('C', () => {});
    });
  });
};

printAll();

console.log(
  `아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있습니다!`
);
  • callback hell : 하지만 코드가 길어질수록 복잡해지고 가독성이 낮아진다.
// 터미널에 `node index.js`를 입력하여 비동기 코드가 작동하는 순서를 확인해보세요.
const printString = (string, callback) => {
  setTimeout(function () {
    console.log(string);
    callback();
  }, Math.floor(Math.random() * 100) + 1);
};

const printAll = () => {
  printString('A', () => {
    printString('B', () => {
      printString('C', () => {
        printString('D', () => {
          printString('E', () => {
            printString('F', () => {
              printString('G', () => {
                printString('H', () => {
                  printString('I', () => {
                    printString('J', () => {
                      printString('K', () => {
                        printString('L', () => {
                          printString('M', () => {
                            printString('N', () => {
                              printString('O', () => {
                                printString('P', () => {});
                              });
                            });
                          });
                        });
                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};

printAll();

console.log(
  `아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있지만 코드가 길어질 수록 복잡해지고 가독성이 낮아지는 Callback Hell이 발생하는 단점이 있습니다.`
);

☀️ 비동기로 작동하는 코드를 제어하는 방법 - Promise

  • Promise는 class
    -- new 키워드를 통해 Promise 객체 생성
    -- 비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데 이 콜백 함수는 (resolve), (reject)함수를 인수로 전달 받는다.
    -- 코드가 정상적으로 처리 되었다면 resolve 함수 호출
    -- 함수를 호출하고 에러가 발생했을 경우 reject 함수 호출
let promise = new Promise((resolve, reject) => {
	// 1. 정상적으로 처리되는 경우
	// resolve의 인자에 값을 전달할 수도 있습니다.
	resolve(value);

	// 2. 에러가 발생하는 경우
	// reject의 인자에 에러메세지를 전달할 수도 있습니다.
	reject(error);
});

✔️ Promise 객체의 내부 프로퍼티

  • new Promise가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖지만 직접 접근할 수 없고 .then, .catch, .finally 메서드를 사용해야 접근 가능
  • state : 기본 상태는 pending(대기). 비동기 처리를 수행할 콜백함수(executor)가 성공적으로 작동했다면 fulfilled(이행)로 변경, 에러 발생 시 reject(거부)
  • Result : 처음인 undefined. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하여 resolve(value)가 호출되면 value로, 에러가 발생하여 reject(error)가 호출되면 error로 변한다.

✔️ then, catch, finally

  • then : executor에 작성했던 코드들이 정상적으로 처리가 되었따면 resolve 함수를 호출하고 .then 메서드로 접근할 수 있다.
    -- 또한, .then 안에서 리턴한 값이 Promise면 Promise의 내부 프로퍼티 result를 다음 .then의 콜백 함수의 인자로 받아오고, Promise가 아니라면 리턴한 값을 .then의 콜백 함수의 인자로 받아올 수 있다.
let promise = new Promise((resolve, reject) => {
	resolve("성공");
});

promise.then(value => {
	console.log(value);
	// "성공"
})
  • catch : executor에 작성했던 코드들이 에러가 발생했을 경우에는 reject 함수를 호출, .catch 메서드로 접근 가능
let promise = new Promise(function(resolve, reject) {
	reject(new Error("에러"))
});

promise.catch(error => {
	console.log(error);
	// Error: 에러
})
  • fanally : executor에 작성했던 코드들이 정상 처리 여부와 상관없이 .fanally 메서드로 접근 가능
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가 필요한 경우는 비동기 작업을 순차적으로 진행해야 하는 경우. .then, .catch, .finally의 메서드들은 Promise를 리턴하기 때문에 .then을 통해 연결할 수 있고 에러가 발생할 경우 .catch 로 처리 가능
let promise = new Promise(function(resolve, 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()

  • 여러개의 비동기 작업을 동시에 처리하고 싶을 때 사용, 배열을 인자로 받음
  • Promise 중 하나라도 에러가 발생하게 되면 나머지 Promise의 state와 상관없이 즉시 종료
// 기존
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 Hell : 콜백함수와 마찬가지로 코드가 길어질수록 복잡해지고 가독성이 낮아짐
const printString = (string) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(string);
    }, Math.floor(Math.random() * 100) + 1);
  });
};

const printAll = () => {
  printString('A').then((value) => {
    console.log(value);

    printString('B').then((value) => {
      console.log(value);

      printString('C').then((value) => {
        console.log(value);

        printString('D').then((value) => {
          console.log(value);

          printString('E').then((value) => {
            console.log(value);

            printString('F').then((value) => {
              console.log(value);

              printString('G').then((value) => {
                console.log(value);

                printString('H').then((value) => {
                  console.log(value);

                  printString('I').then((value) => {
                    console.log(value);

                    printString('J').then((value) => {
                      console.log(value);

                      printString('K').then((value) => {
                        console.log(value);

                        printString('L').then((value) => {
                          console.log(value);

                          printString('M').then((value) => {
                            console.log(value);

                            printString('N').then((value) => {
                              console.log(value);

                              printString('O').then((value) => {
                                console.log(value);

                                printString('P').then((value) => {
                                  console.log(value);
                                });
                              });
                            });
                          });
                        });
                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
};

printAll();

console.log(
  `아래와 같이 Promise를 통해 비동기 코드의 순서를 제어할 수 있지만 Callback 함수와 같이 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Promise Hell이 발생하는 단점이 있습니다.`
);

✔️ Async/ Await

  • 복잡한 Promise 코드를 간결하게 작성
  • 함수 앞에 async 키워드를 사용하고 async 함수 내에서만 await 키워드를 사용
// 함수 선언식
async function funcDeclarations() {
	await 작성하고자 하는 코드
	...
}

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

// 화살표 함수
const ArrowFunc = async () => {
	await 작성하고자 하는 코드
	...
}
const printString = (string) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
      console.log(string);
    }, Math.floor(Math.random() * 100) + 1);
  });
};

const printAll = async () => {
  await printString('A');
  await printString('B');
  await printString('C');
};

printAll();

console.log(
  `Async/Await을 통해 Promise를 간결한 코드로 작성할 수 있게 되었습니다.`
);

☀️ Node.js

  • 비동기 이벤트 기반 JavsScript 런타임
  • 모듈이란? 어떤 기능을 조립할 수 있는 형태로 만든 부분
  • Node.js 내장 모듈을 사용하는 방법
    -- (링크 : https://nodejs.org/dist/latest-v16.x/docs/api/) 참조
  • 3rd-party 모듈 : js에서 공식적으로 제공하는 빌트인 모듈이 아닌 모든 외부 모듈
    -- 사용하는 방법 : npm 사용하여 다운로드 -> 설치 후엔 Node.js 내장 모듈을 사용하듯 require 구문을 통해 사용가능
// underscore 모듈 설치
npm install underscore

// underscore 묘듈 사용
const _ = require('underscore');

☀️ Node.js 공식 문서 가이드

✔️ fs.readFile을 통해 알아보는 Node.js 공식문서 가이드

  • 메서드 fs.readFile은 로컬에 존재하는 파일을 읽어옴

✔️ fs.readFile(path[, options].[])

  • 메서드 fs.readFile은 비동기적으로 파일 내용 전체를 읽는다.
  • path 에는 파일 이름을 전달인자로 받는다.
    -- 네 가지 종류의 타입을 넘길 수 있지만 일반적으로 문자열(string)의 타입을 받는다
path \<string> | \<Buffer> | \<URL> | \<integer>
  
// 'etc/passwd'라는 파일을 불러오는 예제
fs.readFile('/etc/passwd', ..., ...)
  • 대괄호를 감싼 두 번째 전달인자 options는 넣을 수도 있고, 넣지 않을 수도 있다.
    -- 대괄호는 선택적 전달인자를 의미
    -- 문자열 또는 객체 형태로 받을 수 있다.
options\<Object>|\<string>

// /etc/passwd 파일을 'utf8'을 사용하여 읽습니다.
// 두 번째 전달인자 options에 문자열을 전달한 경우
fs.readFile('/etc/passwd', 'utf8', ...);
            
            
// 두 번째 전달인자 options에 객체를 전달한 경우
let options = {
  encoding: 'utf8', // utf8 인코딩 방식으로 엽니다
  flag: 'r' // 읽기 위해 엽니다
}

// /etc/passwd 파일을 options를 사용하여 읽습니다.
fs.readFile('/etc/passwd', options, ...) 
  • 콜백 함수를 전달할 때, 파일을 읽고 난 후에 비동기적으로 실행되는 함수
    -- 콜백 함수에는 두 가지 매개변수 존재
    -- 에러 발생 시 err 는 null이 됨
    -- data 에 문자열이나 Butter라는 객체가 전달됨

☀️ 페어 프로그래밍 - 1.타이머 API / 2.fs모듈

1.타이머 API는 Promise 실행 함수 resolve와 reject의 의미, net Promise()를 통해 생성한 Promise 인스턴스에 어떤 메서드가 존재하는지, 용도는 무엇인지, Promise의 세 가지 상태(.then, .catch, .finally)의 의미, async/await의 의미등을 직접 실습할 수 있는 자료가 제공됨

2.fs모듈은.. 진짜룽 어려워서 오늘 저녁에 공부가 더 필요함!

☀️ 25일차 수업을 마치며...

넘 어려워요... 진짜루.. 비동기 부분 오늘 저녁에 보충 수업이 필요하다. 오늘 저녁시간엔 운동을 마치고 와서.. 유튜브 영상들을 좀 찾아보고 내용을 보충해야겠다.

profile
[무엥일기] 무엥,,, 내가 머쨍이 개발자가 될 수 이쓰까,,,

0개의 댓글