25일차

JiHun·2023년 5월 15일

부트캠프

목록 보기
22/56

💼 동기 vs 비동기

기본적으로 코드들은 한 작업을 끝내고 다른 작업을 시작한다. 이 상황을 동기적(synchronous) 이라고 한다. 다른 말로 표현하면 한 작업이 끝날 때까지는 다른 작업은 실행하지 않는다는 뜻이다. 한 작업이 실행될 때 다른 작업을 막는 것을 블로킹(blocking)이라고 한다. 이것은 어쩌면 비효율적일 수 있다.

비동기는 한 작업이 끝나길 다른 작업이 기다리지 않고 다음 코드를 수행하는 것을 의미한다.

💼 비동기 JavaScript

브라우저에서 제공하는 Web API이며 비동기로 작동한다.

🗂️ setTimeout(callback, millisecond)

일정 시간 후에 함수를 실행한다.

setTimeout(function() {
	console.log('1초 후 실행');
}, 1000)

첫번째 파라미터로 함수를 받고, 두번째 인자로 밀리초를 받는다. setTimeout 함수가 실행되면 밀리초 이후에 첫번째 파라미터 함수를 실행한다.
이 함수의 리턴값은 임의의 타이머 ID다.

🗂️ clearTimeout(timerId)

setTimeout 타이머를 종료한다.

const timer = setTimeout(function() {
	console.log('1초 후 실행');
}, 1000)

clearTimeout(timer);

리턴 값이 타이머 ID이기 때문에, 매개변수로 타이머 ID를 전달하면 그 함수를 종료한다.

🗂️ setInterval(callback, millisecond)

일정한 시간을 갖고 함수를 반복적으로 실행한다.

setInterval(function() {
	console.log('1초마다 실행');
}, 1000)

첫번째 파라미터는 콜백 함수(반복적으로 실행할 함수), 두번째 파라미터는 반복할 시간(밀리초)를 전달한다.

🗂️ clearInterval(timerId)

setInterval 타이머를 종료한다.

const timer = setInterval(function() {
	console.log('1초마다 실행');
}, 1000);

cleartInterval(timer);

setInterval 리턴값이 타이머 ID이기 때문에, clearInterval의 전달인자로 타이머 ID를 보내주면 타이머가 종료된다.

💼 Callback

비동기 코드는 코드가 작성된 순서대로 작동되는 것이 아니라 동작이 완료되는 순서대로 작동한다. 그래서 비동기 코드들의 순서를 예측할 수 없다.
비동기로 작동하는 코드를 제어하는 방법으로 Callback을 사용한다. Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있다.

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();  

printString 함수는 각 함수마다 다른 딜레이 시간을 가지고 있다. 하지만 콜백을 이용해서 다른 시간이어도 순서를 정해서 실행할 수 있다.

하지만 이렇게 계속 작업하다 보면 가독성이 심하게 떨어지고 depth가 깊어진다. Callback Hell에 빠질 수가 있다.

🗂️ Promise

Promise라는 class를 이용해 비동기로 작동하는 코드를 제어할 수 있다. 또한 콜백 지옥도 빠져나올 수가 있다. 비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데 이 콜백 함수는 resolve, reject 함수를 인수로 전달받는다.

📑 Promise 객체의 내부 프러퍼티

new Promise가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖는다. 하지만 직접 접근할 수 없고, .then, .catch, .finally의 메서드를 사용해야 접근이 가능하다.

class Promise {
	constructor(resolve, reject) {
    	this.resolve = function() {}
      	this.reject = function() {};
      	this.state = pending;
        this.result;
    }
  	
  	then(reslove) {
      	...
   		return this;
    };
	catch(reject) {
		...
		return this;
    };
	finally() {};
}

어떻게 Promise class가 어떻게 생긴진 모르겠지만 아마 이렇게 생겼을 것이다.

📑 State

기본 상태는 pending(대기). 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동했다면, 상태는 fulfilled(이행)로 변경되고, 에러가 발생했다면 rejected(거부) 상태가 된다.

📑 Result

기본 상태는 undefined. 콜백 함수(executor)가 성공적으로 작동하면resolve(value)가 호출되며 value로, 에러가 발생하여 reject(error)가 호출되면 error로 변한다.


📑 then

executor에 작성했던 코드들이 정상적으로 처리가 되었다면 resolve 함수를 호출하고, .then 메서드로 접근할 수 있다. 또한 .then 안에서 리턴한 값이PromisePromise의 내부 프로퍼티 result를 다음 .then 의 콜백 함수의 인자로 받아로고, Promise가 아니라면 리턴한 값을 .then 의 콜백 함수의 인자로 받아올 수 있다.

let promise = new Promise(() => {   // 변수 promise에 Promise 객체 할당.
	resolve("성공")
});

promise.then(value => {            // Promise 객체는 .then을 쓸 수 있다.
	console.log(value);              // "성공"
})

📑 catch

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

let promise = new Promise(function(resolve, reject) {
	reject(new Error("에러"));
})

promise.catch(error => {
	console.log(error);
})

📑 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("마지막에 실행되며 성공, 실패 뭐가 오든 작동");
})

🗂️ Async/Await

JavaScript ES8에서 async/await 키워드를 제공한다. async 함수 내에서만 await를 사용하고 await 키워드 뒤에 오는 Promise 객체가 동작하고 나서야 다음 순서의 코드가 동작한다. 즉, 비동기 코드를 동기적으로 순서를 간단하게 적을 수 있다.

const asyncFunc = async function() {
	await 작성하고자 하는 코드 1          // 비동기를 동기적으로
    await 작성하고자 하는 코드 2
}

📝 마지막으로

비동기에 대해서도 조금 어려운데 Promise 객체를 이해하는 데 있어 좀 어렵고, 복잡하다. 이것 또한 반복으로 익숙해지면 되겠지...

profile
안녕하세요. 프론트엔드 개발자 송지훈입니다.

0개의 댓글