JS Reboot - 비동기

코스·2020년 9월 27일
1

JS Reboot

목록 보기
3/4
post-thumbnail
post-custom-banner

🤔 비동기가 필요한 이유.jpg

최근 글쓴이는 우아한테크러닝 3기 교육을 받고있습니다. 이전에 사용했던 Javascript 스펙들을 다시보게 되면서 배운 점들을 시리즈 글로 올려볼까 합니다. 글에 오류가 있거나 궁금한 점이 있다면 언제든 댓글로 알려주세요!

비동기

Javascript에서 가장 큰 허들으로 뽑히는 스펙이 바로 비동기 로직입니다. 왜 비동기스러운 로직을 왜 만든것일까요? 어떻게 보면 인간의 생각는 동기적으로 이루어져있는데, 비동기를 도입하는게 많이 어려울 수 있습니다. 다만 차근차근 비동기 로직이 발전되는 과정을 살펴본다면 비동기를 더욱 쉽게 이해할 수 있을겁니다.

비동기의 개념

Javascript의 주요 특징으로는 싱글스레드라는 것입니다. 모든 로직을 하나의 스레드로 돌아가기 때문에, 한 작업이 시작되면 끝나기 전까지는 다음 작업을 시작하지 못합니다. 이와 같이 작업하는 방식을 동기(Synchronous)라고 합니다.

사실 대부분의 웹 서비스는 동기적으로 작업을 수행하면 안됩니다. 간단한 예시로 동영상을 업로드한다고 한다면, 동영상을 업로드하는 동안 다른 작업을 처리할 수 없게 됩니다. 업로드 하면서 사용자들에게 업로드 진척을 보여줄 수 없는 것입니다!

그렇기 때문에, Javascript 스펙을 만드는 분들은 동기의 반대 개념인 비동기(Asynchronous)를 도입하게 됩니다. 비동기는 동기와 다르게, 한 작업을 진행시켜도, 다른 작업을 시작할 수 있습니다. 다른 말로 한 작업이 작동하는 도중에도 다른 작업을 시작할 수 있는 것입니다!

🤔 싱글스래드로 구성되어있는 Javascript가 비동기를 어떻게 지원하는지는 글이 길어지기 때문에 이번에 설명하지 않을 예정입니다. 관련해서 이 글을 참고해보시면 좋을 것 같습니다!

Callback

setTimeout(function (x) {
  console.log('안녕');
  setTimeout(function (y) {
    console.log('또 안녕');
  }, 2000);
}, 1000);

초기 비동기는 콜백으로 구현되어 있었습니다. 호출한 함수의 작업이 끝나면 콜백 함수가 실행되는 방식으로 이루어져 있는데, 이 구현 방식의 문제점은 코드가 잘 읽히지 않는다는 것입니다.

위의 코드가 작동하는 순서를 한눈에 볼 수 있을까요? 만약 콜백이 두겹이 아니라 수십겹이 겹쳐져 있다면, 이후에 코드를 읽고 유지보수 할수 있을까요? 이런 점을 한단계 푼 스펙이 바로 Promise입니다.

Promise

const p = new Promise((resolve, reject) => {
  console.log("test")
  setTimeout(() => {
    resolve("응답"); // resolve도 클로저로 잡힘!
  }, 1000);
  if (false) { // 에러처리
    reject();
  }
});

console.log("시작!");

p.then(function (r) {
  console.log(r); // 응답
}).catch(function () {
  console.log("error!");
})

Promise는 콜백함수를 받는 것은 이전 방식과 동일하지만, Promise에서 중요한 점은 Promise 객체를 생성할 때 작업이 작동되지 않는다는 것입니다. 위 코드에서는 응답을 출력하는 것보다 시작!을 먼저 출력합니다. Promise는 다음과 같이 작동합니다.

  1. Promise 객체를 생성할 때 콜백함수는 비동기로 처리할 작업을 정의해놓습니다. 이때 콜백함수의 매개변수는 두개가 존재하는데, resolvereject는 이 작업이 끝난 이후 호출하는 함수입니다. 작업을 정의할 때, 작업을 마치면 resolve 함수를 호출하고, 작업에 문제가 생긴다면 reject 함수를 호출해 비동기 작업을 종료하게 해야합니다. 콜백함수이기 때문에 resolvereject는 이후 이 객체가 실행될 때 정의됩니다.
  2. 실제로 Promise 객체를 실행할 때엔 p.then()이 작동될 때입니다. 위 코드에서는 p.then().catch() 형식으로 클로저로 구현되어 있습니다. 이때 Promise의 resolvereject를 정의하는데 then 안에 resolve 함수를, 그리고 catch 안에 reject 함수를 정의하는 것입니다.

그러기 때문에 Javascript에서는 시작이 먼저 출력되는 것입니다. 위 콜백형식으로 구현하는 것과 차이점은, 더욱 사람이 이해하기 쉽게 코드를 구현할 수 있다는 것입니다. 위에서도 이야기했듯 사람의 생각은 동기적으로 생각하기 때문에,

(작성중입니다)

const p = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("응답1");
	}, 1000);
	// reject();
});

const nextp = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("응답2");
	}, 1000);
	// reject();
});

일자로 펴는! 비동기 함수

Async, Await

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function main() {
	console.log(1);
	try {
		const val = await delay(2000); // 2초간 
	} catch(e) {
		console.error(e);
	}
	console.log(2);
}

main은 비동기 처리를 할 수 있는 함수

delay 함수를 호출하면 프로미스 객체가 리턴됨

async 를 붙인 함수 안에서 await를 사용하면

await는 프로미스다. 프로미스가 리졸브 된 값을 저장한다!

리젝트는?? try, catch에 잡힘!

function sayHelloAsync(name) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(name + '님 안녕하세요.')
      resolve(name)
    }, 2000)
  })
}

async function sayByeAsync(name) {
  const result = await sayHelloAsync(name)
  console.log(result + '님 안녕히가세요.')
}

sayByeAsync('Minsu')

참고문서

Hits

profile
잡다한거 하는 개발자
post-custom-banner

0개의 댓글