js/node - 비동기 2

2021. 10. 15. 19:44·SE Bootcamp 내용 정리

비동기 1 내용에 대한 보충 학습

비동기를 처리하는 3가지 방식: callback, promise, async/await

 

여태 우리가 짰던 코드들은 대부분 동기적 구문이다: sync+blocking
ex) for 문
→ 내가 짰던 코드 순서대로 다 끝난 다음에야 그 다음 코드가 실행됨

 

비동기적 처리의 경우: async+non-blocking

 

시작은 순서대로 되었지만, non-blocking이므로 앞의 task의 진행 유무와 관계 없이
다음 task들이 각각 자기들 처리 다 되는대로 끝남

 

node.js에서 callback 함수를 사용할 때는 일종의 convention이 있다(`국룰`(관행) 같은거)

 

가령 예를 들면…

콜백 함수에는 두 가지 파라미터가 존재합니다. 에러가 발생하지 않으면 err 는 null 이 되며, data 에 문자열이나 Buffer 라는 객체가 전달됩니다. data 는 파일의 내용입니다.


...
if(err){
    callback(err, null);
}else{
    callback(null, data);
}

...

비동기가 가장 많이 사용되는 상황: `fetch` ← 네트워크 요청

 

비동기를 처리하는 방식(callback 등)들이 동기를 비동기적으로 만드는 문법 이라기보다는, `예측 불가능한 비동기 작업들을 제어하기 위한 방법` 이라고 이해 하는게 낫다

 

Promise 내용 보충 학습

하나의 task를 봤을때 그 상태를 보고 행동을 제어하는 개념?

 

그 상태: 대기(pending), 이행(fulfilled), 거부(rejected)

 

Promise() 는 executer(실행함수; 일종의 callback)을 첫번째 인자로 받는데 그 executer는 `resolve`와 `reject`를 인자로 가진다

ex)
const asyncTask = new Promise( (resolve, reject) => {
    //여기서 reject만 제어한 경우
    //reject(‘blabla’);
})

asyncTask    // Promise{<rejected>: ‘?’}
//위에서 아무것도 안썻다면 <pending> 상태
// 위에서 resolve만 작성했다면 <fullfilled> 상태가 되어 있음을 확인 가능

fetch API

fetch를 이용한 네트워크 요청

비동기 요청의 가장 대표적인 사례: 네트워크 요청
→ 그 중 URL로 요청하는 경우가 가장 흔한데, 이 URL로 요청하는 것을 가능케 해주는 API
→ fetch API

 

포털 사이트를 예시로 한 fetch 과정 이해

네이버 같은 포털 사이트에서 일어나는 네트워크 요청을 통해 알아보자

 

포털 사이트 메인에는 시시각각 변하는 정보(동적으로 데이터를 받아와야하는 정보)와 늘 고정적인 정보로 구성되어 있다.
→ 동적으로 데이터를 받아와야 하는 정보: 최신 뉴스, 날씨 정보

 

대다수의 웹 사이트는 이렇게 동적으로 데이터를 받아와야 하는 경우, 해당 정보만 업데이트하기 위해 요청 API를 사용한다.
→ 그 중 대표적인 API가 fetch API; 해당 정보를 원격 URL로부터 불러 옴

 

ex) 원격 URL로부터 정보를 받아 와서 특정 DOM 엘리먼트(날씨)를 업데이트하는 과정을 생각해보면, 이를 fetch API를 통해 특정 URL(ex. http://기상청 홈페이지/weather.json)로부터 정보를 받아 옴

 

이러한 fetch API 과정은 비동기로 이루어 지므로, 다소 시간이 걸릴 수 도 있다
→ blocking이 발생하면 안되므로, 특정 DOM에 정보가 표시될 때 까지 로딩 창을 대신 띄우기도 함

 

fetch API 사용법

다음과 같이 사용한다

let url = “https://어떠한주소의url”;

fetch(url)
  .then((response) => response.json()) 
  // 자체적으로 json() 메소드가 있어, 응답을 JSON 형태로 변환시켜서 다음 Promise로 전달
  .then((json) => console.log(json)) // 콘솔에 json(전달인자)을 출력합니다
  .catch((error) => console.log(error)); // 에러가 발생한 경우, 에러를 띄웁니다

→ 이렇게 하면 특정 URL로부터 정보를 json화해서 가져올 수 있다.

 

보다시피, fetch API도 Promise의 형식으로 이루어져 있다.

 


실습을 통한 비동기 이해

fs 모듈 사용하기

fs.readFile() 를 이용해서 callback, Promise, async/await를 사용하는 연습을 해보자

 

callback 함수를 이용한 fs 모듈

ex) callback 함수를 이용한 fs 모듈의 예

...

fs.readFile(filePath, 'utf8', (e, d)=> {
if(err){
callback(err, null)
}else{
callback(null,d)
}
});

...
//err 가 발생한 경우 callback을 해주는데 이때 인자값은 callback(err, null)
//→ callback의 첫번째 인자가 `에러`가 들어가고, 두번째 인자(data가 들어갈 자리)는 null
// err 가 발생하지 않은 경우→ 즉, data가 정상적으로 들어가서 작동하는 경우이므로
// callback(null, d)    → 에러가 들어갈 자리는 null, 2번째 인자는 data(d)

 

PromiseAll을 이용한 fs 모듈

Promise.All의 첫번째 인자로 배열을 받는다는 게 핵심
→ 이 배열은 요소로 `Promise` 객체를 가진다


ex) Promise.all 이 적용된 코드의 예시
...

return Promise.all([getDataFromFilePromise(user1Path), getDataFromFilePromise(user2Path)])
	.then((v) => {
		let result=[];
		result.push(JSON.parse(v[0]));
		result.push(JSON.parse(v[1]));
		return result;
		});

...

 

Q. Promise.all 의 인자는 어떠한 형태?
→ iterable; Array와 같이 "순회가능한" 객체

 

Q.Promise.all 을 사용할 경우에 then 메소드의 파라미터는 어떠한 형태?
→ 앞에서 시행(이행)한 프로미스값이 파라미터에 들어가며,
(프로미스가 아닌 값을 포함하여) 매개변수로 주어진 순회 가능한 객체에 포함된 모든 값을 담은 배열

 

Q. Promise.all 에 두 개의 Promise 요청이 전달되고, 만일 그 중 하나가 rejected 상태가 되는 경우,

then메소드를 따라가나요, 아니면 catch 메소드를 따라가나요?
→ catch 메소드를 따라간다.

 

why? Promise.all 의 경우 주어진 프로미스 중 하나라도 거부하면,
다른 프로미스의 이행 여부에 상관없이 첫 번째 거부 이유를 사용해 거부한다.

 

async/await를 이용한 fs 모듈

함수의 앞에 async 키워드를 붙이고 해당 함수의 내부에서 Promise 객체 앞에 await를 붙여

비동기적인 async 함수를 동기적으로 작동하게 하는게 포인트!
→ 동기적으로 작동하므로 시간적 부분에서 봤을때 동기식으로 처리되는 것과 같은 시간이 소요된다.

 

await 은 비동기적 task를 동기적으로 동작하도록 해준다
그러므로 작동은 동기적으로 작동이므로

→ 시간적으로 볼때 하나 끝나고, 다음꺼 하고 하듯이 동작

 

오류처리와 관련된 .catch() 블럭은 처리를 못한다
따로 .catch 구문을 써줘야 한다(오류 처리와 관련)


await도 내부적으로 Promise로 동작하므로 반환되는 결과값은 Promise 이다


ex) async/await가 적용된 fs 모듈의 예

…

const readAllUsersAsyncAwait = async () => { //async 키워드를 function 키워드 앞에 써줘야하는데
// 여기서는 익명 화살표 함수로 받고 있으므로 익명 화살표 함수 앞에 async 키워드를 썼음

let result=[];
let parsedData1=JSON.parse(await getDataFromFilePromise(user1Path)); 
//해당 비동기 작업이 끝날때까지 기다린다(await)

let parsedData2=JSON.parse(await getDataFromFilePromise(user2Path));

result.push(parsedData1);
result.push(parsedData2);
return result;
}

...

 

fetch API 실습을 통한 이해

Promise.all 을 활용한 fetch API 작성

Promise.all 은 기본적인 문법이 아래와 같음을 숙지해야 한다

Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});

 

fetch API를 함수 내에서 `Promise.all` 과 함께 작성할 때는 다음과 같이 작성할 수 있다.

ex) fetch API with Promise.all 의 예시

…
const urls = [URL1, URL2];
const fetches = urls.map(url => fetch(url));

return Promise.all(fetches)
.then((responses) => {
return Promise.all(responses.map(response => response.json()));
// Promise.all로 return 해서 전달해야한다 then에 전달할 때는
})
.then((objs) => {
const url1Text = objs[0].data;
const url2Text=objs[1];
return {url1Text, url2Text};
})

…

 

async/await를 활용한 fetch API

await는 비동기 키워드
→ await 키워드는 Promise를 기다리기 위해 사용된다.
→ async function 내부(코드블럭)에서만 사용할 수 있다.

 

Promise 객체를 리턴하는 함수는(async 키워드가 안붙어도) 모두 await로 완료를 기다리게 만들 수 있다.

 

await 키워드는 JavaScript 런타임이 이 라인에서 비동기 코드를 일시 중지하여

비동기 함수 호출이 결과를 반환할 때 까지 기다리게 한다
→ 비동기로 동작하는 함수를 동기적으로 출력되도록 만들어 준다

 


ex) async/await 를 활용한 fetch API 의 예

async 함수의 내부에서…

…

let result={};

// let newsInfo = await fetch(newsURL).then((v) => v.json());
// let weatherInfo =await fetch(weatherURL).then((v) => v.json());

// result.news = newsInfo.data;
// result.weather = weatherInfo;

// return result;

…

 

 


비동기2에 대한 보충 내용

Promise.all

→ 여러 개의 Promise를 처리하기 위해 사용
반복문, 고차함수의 형태랑 비슷한 느낌?

 

핵심: Promise.all의 첫번째 인자에 `Promise들이 담긴 배열`이 들어 간다

 

일반적인 Chaining vs. Promise.all

 

* chaining: 순서가 중요한 경우 → 결과가 꼭 다 끝나고 다음 task에 사용되어야 하는 경우

* Promise.all: 같은 task를 동시에 처리해야 하는 경우

// 어디에 무엇을 써야 하는가는 정해진 것은 없고 개발자의 판단(선택)

 

Promise도 객체의 한 형태긴 하다

typeof new Promise(() => {}) // ‘object’

 

Promise인지 판별하기 위해서는 instanceof 키워드를 이용하여 판별은 가능하다.(허나, instanceof 키워드 사용시 주의!)

new Promise(() => {}) instanceof Promise // true

 

async / await

await: 해당 작업이 끝날 때 까지 `기다리는 것`

 

await 은 비동기적 task를 동기적으로 동작하도록 해준다

그러므로 작동은 동기적으로 작동이므로(시간적으로 볼때 하나 끝나고, 다음꺼 하고 하듯이)

 

→ async 키워드는 함수 앞에 붙여주고, await 키워드는 함수 내부의 Promise 객체 앞에 붙여 준다

 

fetch API

fetch API는 Node.js에서 지원을 안하므로(작동x), 브라우저에서 test해야 한다는 점을 잊지 말자

저작자표시 (새창열림)

'SE Bootcamp 내용 정리' 카테고리의 다른 글

http/네트워크 – REST API & Postman  (0) 2021.10.19
http/네트워크 - 기초  (0) 2021.10.18
js/node - 비동기 1  (0) 2021.10.15
자료구조/알고리즘 - 자료구조 기초 2(Graph, Tree Search Algorithm)  (0) 2021.10.13
자료구조/알고리즘 - 자료구조 기초(Stack, Queue, Graph, Tree, BST)  (0) 2021.10.11
'SE Bootcamp 내용 정리' 카테고리의 다른 글
  • http/네트워크 – REST API & Postman
  • http/네트워크 - 기초
  • js/node - 비동기 1
  • 자료구조/알고리즘 - 자료구조 기초 2(Graph, Tree Search Algorithm)
레실이
레실이
  • 레실이
    레실이의 티스토리
    레실이
  • 전체
    오늘
    어제
    • 분류 전체보기 (88) N
      • SE Bootcamp 내용 정리 (63)
      • 알고리즘 연습 (7)
      • Project 주저리 (4)
      • 기술 면접 source (3)
      • 개발 일상 (9) N
      • 생성 AI 활용 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Linux
    자료구조
    PickAndDrink
    ubuntu
    객체
    node
    JS
    DOM
    promise
    중복 순열
    IT
    JavaScript
    ORM
    useState
    인증/보안
    CSS
    node.js
    Ajax
    fastapi
    문자열
    react
    mongoDB
    MVC
    Python
    state
    CORS
    데이터베이스
    CSR
    useRef
    알고리즘
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
레실이
js/node - 비동기 2
상단으로

티스토리툴바