웹개발 복습 정리 21 : 비동기

Kimhojin_Zeno·2023년 1월 24일
0

웹개발 복습 정리

목록 보기
21/30

강의 섹션 27

Call Stack

스택은 컴퓨터 과학의 기본 데이터 구조이다.

후입선출(LIFO) 데이터 구조.

콜스택은 자바스크립트 해석기가 사용하는 메커니즘으로

여러 함수를 호출하는 스크립트에서 해당 위치를 추적한다.

그래서 자바스크립트가 위치를 알 수 있고 책 속의 손가락 같은 것이다.

자바스크립트는 콜 스택에 함수 호출을 추가하고

값이 반환될 때마다 삭제한다.

Loupe라는 도구로 시각적으로 돌아가는 것을 볼 수 있다.

크롬 개발자도구에 debugger로도 볼 수 있다.

Single thread

javascript는 single thread이다.

무슨 말이냐면, 한 번에 한줄의 코드만 실행한다는 것.

동시에 여러개 작업을 못한다.

서버에 요청을 보내면, 그걸 받기 전까지 다음 코드를 실행하지 못함.

그러나 현실에선 그렇지 않다.

브라우저나 node.js와 같은 런타임(사용환경)이 자바스크립트가 못하는 그 작업을 해줌.

브라우저에는 Web API라는 것이 있다. javascript가 Web API를 호출하여

브라우저가 대신 처리해주는 것이다. setTimeout과 같은 ‘몇초 있다 처리해줘’ 같은

작업을 브라우저가 해준다.

작동하는 방식은 javascript의 콜스택이 web api 함수를 인식하고 브라우저에 전달.

‘3초 후 이 작업 해줘’. 그러면 브라우저는 3초 후에 콜스택에 추가한다.

자바스크립트는 그걸 받아서 다시 실행.

console.log('first') // 1. 자바스크립트가 첫번째로 실행.
setTimeout(() => { // 2. 이건 브라우저에 보냄
		console.log('print after 3 seconds'); //4. 브라우저가 3초 후 자바스크립트에 알려줌. 그럼 자바스크립트가 콜백 실행.
}, 3000);
console.log('second'); // 3. 자바스크립트가 두번째로 실행.

실제 작동하는 메커니즘은 두가지다.

  1. 브라우저에 이러한 함수가 있고 자바스크립트를 위해 실행.
  2. 바로 실행되지 않고 나중에 실행되는 함수를 전달하는 콜백.

Callback Hell (콜백 지옥)

setTimeout(function () {
    setTimeout(function () {
        setTimeout(function () {
            setTimeout(function () {
                setTimeout(function () {
                    setTimeout(function () {
                        // ...
                    });
                });
            });
        });
    });
});

콜백 지옥이란 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 가독성이 떨어지고, 수정도 어려워지게 되는 것.

이벤트 처리나 서버 통신과 같은 비동기 작업을 제어하기 위해서 콜백함수를 쓰는데

이를 해결하기 위해 Promises 와 비동기함수를 쓴다.

Syncronous & Asynchronous

동기와 비동기

동기: 요청을 보낸 후 해당 요청의 응답을 받은 후 다음 동작으로 넘어감

비동기: 요청을 보낸 후 응답과 관계없이 다음 동작으로 넘어감.

동기는 번호표 뽑고 기다렸다 앞에 사람 끝나야 내 차례가 온다.

비동기는 식당에서 음식주문해서 먼저 나오는 순으로 먹는 방식.

Promise

promise는 어떤 연산, 비동기 연산이 최종적으로 완료 혹은 성공했는지

실패했는지를 알려주는 객체이다.

가장 흔한 사례는 요청을 보내 다른 위치에서 데이터를 받는 것이다.

날씨 API등에 요청을 보내서 데이터를 받을때 때로는 시간이 오래 걸리고, 접속 자체가 안되고, 접근 권한이 없다든지 url을 잘못 쓴다던지 다양한 이유로 접속이 안된다.

Promise는 값이나 오류에 대한 최종 약속이다.

pending : 기다리는 상태

resolved : 성공

rejected : 실패

promise는 비동기적 값이 최종적으로 resolved일지 rejected일지 알려준다.

핵심은 rejected 또는 resolved일 때 특정 코드를 실행한다는 것.

함수 안에 콜백을 전달하는 게 아니라 Promise 객체 자체에 콜백을 첨부해야 한다.

그리고 함수가 Promise 객체를 반환하기를 기다린다

then, catch

실제 사용할땐 이렇게 쓴다.

const request = fakeRequestPromise('aef.com/api');

request
	.then(() => {
			console.log("it worked!")
	}) // 요청이 resolved일때 실행
	.catch(() => {
			console.log("oh no, error!")
	}) // 요청이 rejected일때 실행

성공하면 then으로, 에러가 나면 catch로 간다. 분기점을 만드는것.

콜백을 사용해서 콜백지옥으로 코드를 구성하는 것보다 훨씬 알기 쉽다.

분기점 안에 분기점으로 중첩할 수 있다.

const request = fakeRequestPromise('aef.com/api');

request
	.then(() => { // 요청이 resolved일때 실행
			console.log("it worked!")
			fakeRequestPromise('aef.com/api/page2') // 두번째 분기점
					.then(() => {
							console.log("it worked! (page2)")
					})
					.catch(() => {
							console.log("oh no, error! (page2)")
					})
	}) 
	.catch(() => { // 요청이 rejected일때 실행
			console.log("oh no, error!")
	}) 

중첩이 여러단계로 되면 더 간단하게 바꿀 수 있다.

fakeRequestPromise('aef.com/api');
	.then((data) => { 
			console.log("it worked!(page1)")
			console.log(data)
			return fakeRequestPromise('aef.com/api/page2') //요청을 반환
	}) 
	.then((data) => { 
			console.log("it worked!(page2)")
			console.log(data)
			return fakeRequestPromise('aef.com/api/page3') 
	}) 
	.then((data) => { 
			console.log("it worked!(page3)")
			console.log(data)
			return fakeRequestPromise('aef.com/api/page4') 
	}) 
	.catch((err) => { //어디서든 Promise가 reject되면 여기로 온다.
			console.log("oh no, error!")
			console.log(err) //에러의 내용을 표시
	}) 

맨 마지막에 catch 한번으로 모두 커버된다.

.then을 연쇄해서 사용하면 resolved 됐을 때는 다음 단계로 넘어가고,

rejected되면 단계에 상관없이 바로 마지막 .catch로 간다.

new Promise

new Promise((resolve, reject) => {
		//매개변수 이름은 다르게 지어도 됨.
		resolve() //첫번째가 resolved, 
		reject() // 두번째가 rejected 
})

어디서든 resolve를 호출하면 Promise가 resolve된다

어디서든 reject를 호출하면 Promise가 reject된다

new연산자를 이용하여 Promise를 직접 만들수 있다.

const fakeRequest = (url) => {
		return new Promise((resolve, reject) => { //promise객체를 리턴
				const rand = Math.random();
				setTimeout(() => {
						if(rand<0.7) {
								resolve('data is here!'); // 0.7보다 작으면 resolved
						}
						reject('req error!!'); // 그 외에는 rejected 상태가 된다.
				}, 1000)
		})
}

fakeRequest('/dogs/1')
		.then((data) => { //resolved가 되면 다음이 실행. 'data is here!'이 전달된다.
				console.log('done request!')
				console.log('data is:',data)
		})
		.catch((err) => {  // rejected가 되면 다음이 실행. 'req error!!'가 전달된다.
				console.log('oh no', err)
		})

Async Function

비동기 함수

대단한건 아니고 깔끔한 코드 작성을 돕는다.

Promise 위에 적용된다. 구문에 뿌리는 설탕.

Syntax “makeup” for promises

async와 await 두가지가 있다.

async

함수를 비동기 함수로 선언하는 키워드

async 함수는 언제나 promise를 return한다

(return new Promise 할 필요가 없음)

async funtion hello() {  //함수 선언

}

const sing = async () => { //화살표 함수도 가능

}

async 함수가 value를 return하면 promise는 resolved가 됨.

const sing = async () => {
		return 'la la la'
}

sing().then((data) => {
	console.log('promise resolved with:', data)
})

//-> promise resolved with: la la la

async 함수가 에러를 return하면 promise는 rejected가 된다.

const sing = async () => {
		throw "oh no, problem"
		return 'la la la'
}

// rejected. "oh no, problem"

기본적으로 비동기 함수에 오류가 있으면 promise의 상태는 실패로 뜬다.

await

Promise가 해결될 때까지 실행을 멈추는 키워드.

await 키워드는 비동기 코드를 쓰면서 동기적으로 보이게 해준다.

기다리게 하는 역할.

Promise가 값을 반환할 때까지 기다리기 위해 비동기 함수의 실행을 일시 정지시킨다.

비동기 함수에서만 적용하기 때문에 async와 await는 한 쌍이다.

const delayedColorChange = (color, delay) => {
		return new Promise((resolve,reject) => {
				setTimeout(() => {
						document.body.style.backgroundColor = color;
						resolve();
				}, delay)
		})
}
// 콜백을 이용한 배경색 바꾸는 함수.

delayedColorChange('red', 1000) 
		.then(() => delayedColorChange('orange', 1000))
		.then(() => delayedColorChange('yellow', 1000))
		.then(() => delayedColorChange('green', 1000))
		.then(() => delayedColorChange('blue', 1000))
		.then(() => delayedColorChange('indigo', 1000))
		.then(() => delayedColorChange('violet', 1000))

//이렇게 .then을 이용해서 앞에 색으로 바뀐 후 차례대로 바뀌도록 할수있다.

async function rainbow() {
		await delayedColorChange('red', 1000)
		await delayedColorChange('orange', 1000)
		await delayedColorChange('yellow', 1000)
		await delayedColorChange('green', 1000)
		await delayedColorChange('blue', 1000)
		await delayedColorChange('indigo', 1000)
		await delayedColorChange('violet', 1000)
}

// async와 await를 사용하면 빨간색이 실행 되기를 기다렸다가 실행된후 다음으로 넘어간다.

Promise를 반환하는 함수나 Promise만 await를 쓴다.

await 키워드는 코드를 깔끔하게 만들어준다.

try, catch

에러 핸들링

기다리고 있는 Promise가 reject되면 어떻게 되나?

try {
		asdfoawef.log('adfs') //이런 함수는 없다.
} catch (e) {
		console.log('its ok', e)
}

try문에 오류가 될 코드를 적으면

catch 문에서는 어떻게 처리할지 정의한다

e는 오류를 나타낸다.

async function makeTwoRequests() {
		try {
				let data1 = await fakeRequest('/page1');
				console.log(data1);
				let data2 = await fakeRequest('/page2');
				console.log(data2);
		} catch (e) {
				console.log('caught an error!')
				console.log('error is:', e)
		}
}
profile
Developer

0개의 댓글