브라우저 동작방식
가끔 가다 javascript 는 비동기적 실행이 되는 (병렬처리가 되는) 언어다 라고 하는 사람들이 있는데
javascript는 동기적으로 실행 되는 프로그래밍 언어임.
비동기적으로 코드를 실행해주는 키워드들이 있긴하지만 자바스크립트는 동기적으로 동작함.
예를들면 setTimeout 함수나 eventListener, ajax 요청 등과 같이 오래 걸리는 애들은 비동기적으로 처리됨.
근데 이건 자바스크립트가 얘네를 비동기적으로 처리하는게 아니라 브라우저가 그렇게 처리해주는 것.
무슨말인지 이해하려면 브라우저가 작동하는 원리를 알아야 하는데
예를들어 이런 코드를 실행한다고 가정하자.
const a = 2;
const b = 3;
const c = 4;
console.log(a);
setTimeout(() => {
console.log(b);
}, 2000);
console.log(c);
브라우저가 이런 코드를 받으면 일단 stack 으로 보내고 heap memory 에 있는 변수나 함수들을 참조해서 stack 에 있는 코드들을 순서대로 하나씩 실행을 하는데 오래걸리는 애들( queue로 보내기로 약속된 애들 )은 일단 무조건 묻지도 따지지도않고 queue로 보낸다.
왜냐면 stack 이 하나라 한번에 한 명령만 실행을 할수 있기때문에 오래걸리는 애들 처리하느라 앞에서 막고 있으면 뒤에 있는 애들 실행이 안되고 브라우저가 멈춰버림.
위 코드를 브라우저 입장에서 보자면은
stack 이 비었을때만 queue 에 있는 대기가 끝난 코드들을 stack 으로 올려서 처리를 해주기 때문에 절대 stack 이 바쁘게 만들면 안됨.
예를들어 stack 에 동기적으로 실행하는 10초 걸리는 코드를 올리면 queue 에 있는 이벤트리스너, ajax 같은 애들이 10초동안 동작을 못함, 버튼 이런거 눌러도 동작을 안한다는 소리임.
여튼 위 설명이 대략적으로 브라우저가 코드를 실행하는 방식임.
그럼 자바스크립트에서 비동기나 동기를 순차적으로 처리하고 싶으면 어떻게 하나요 ?
callback function 혹은 promise 혹은 async await 쓰면 됩니다.
콜백 함수
콜백함수가 뭔가요?
그냥 별거 없고 함수안에 들어있는 함수를 콜백함수라고 부름
근데 이 콜백함수란 놈이 어떻게 비동기 코드나 동기 코드를 순차적으로 실행할수 있는지 알아봅시다.
예를들어 이런 코드가 있다고 합시다.
function firstFunc() {
console.log('first Function');
/*
여기에 오래걸리는 함수 (setTimeout,ajax,이벤트리스너 등) 들어있음
*/
}
function secondFunc() {
console.log('second Function');
/*
여기에 오래걸리는 함수 (setTimeout,ajax,이벤트리스너 등) 들어있음
*/
}
firstFunc();
secondFunc();
이 코드를 실행하면 firstFunc() 가 실행되고 secondFunc() 가 실행될거 같지만 실제로는
오래걸리는 함수가 어떤게 먼저 처리되느냐에 따라 firstFunc()가 먼저 실행 될수도, secondFunc()가 먼저 실행 될수도 있음.
즉, 실행순서가 보장되지 않음.
그런데 콜백 함수를 이용하면
function firstFunc(callback) {
console.log('first Function');
callback();
}
function secondFunc() {
console.log('second Function');
}
firstFunc(secondFunc);
무조건 순서가 'first Function' 이 출력되고 'second Function'이 출력됨.
즉, 순서가 보장이 됨.
근데요, 콜백함수는 한가지 문제가 있음.
만약 순서를 보장해야할 함수가 많다면 ??
firstFunc(function(){
secondFunc(function(){
thirdFunc(function(){
fourthFunc(function(){
fifthFunc(function(){
......
})
})
})
})
})
흔히 말하는 콜백 지옥을 경험할 수 있음.
코드는 옆으로 길어지면 길어질수록 읽기 힘들어짐.
그래서 이걸 해결하기 위해서 ES6 신문법인 Promise 를 사용합니다.
Promise
우선 알아둘것이 promise 나 async await 자체를 비동기나 동기와 상관이 있다고 생각하는 분들이 계신데
promise , async await 자체는 동기나 뭐 비동기 이런애들이랑 전혀 상관 없구요 그냥 디자인 패턴일 뿐임미다.
그 얘기는 좀 이따 해보기로 하고 일단 promise 어떻게 쓰는지 부터 알아봅시다.
const promiseObj = new Promise(function (resolve, reject) {
// 성공,실패 판정해 주는곳
});
promiseObj
.then(function () {
// 성공시 실행할 코드
})
.catch(function () {
// 실패시 실행할 코드
});
이게 기본적인 promise 생김새인데요.
프로미스 객체를 생성할때 콜백함수에서 성공, 실패를 판정해주고
성공하면 then 에 있는 콜백함수 실행,
실패하면 catch 에 있는 콜백함수 실행.
이게 끝입니다. 간단하쥬 ?
자 그럼 성공, 실패 판정을 어케 하느냐
그냥 안에서 resolve(); 하면 성공 이고, reject(); 하면 실팹니다.
그리고 꼭 resolve 나 reject 라고 안하고 작명 마음대로 해도됨.
순서만 잘 지켜주면 됌.
아그리고 resolve() 나 reject() 의 소괄호 안에 연산결과 넣어주면 그게 then 이나 catch 의 콜백함수로 전달됨.
실제로 어떻게 쓰는지 예를 들어봅시다.
예를 들어 10초동안 걸리는 어려운 연산이 있다고 상상해봅시다.
const promiseObj = new Promise(function (resolve, reject) {
const tenSecondsCalc = 1 + 1;
resolve(tenSecondsCalc);
});
promiseObj
.then(function (result) {
console.log(result); //2
})
.catch(function () {
// 실패시 실행할 코드
});
1 + 1 이라고 적었지만 10초 걸리는 연산이라고 상상해봅시다.
resolve() 를 반환하면서 결과값을 넣어서 반환했습니다.
그럼 then 에 있는 콜백함수가 실행되면서 resolve 에서 준 결과값이 전달되었스빈다.
끝입니다.
근데요, 프로미스 객체는 사실 3가지 상태가 있습니다.
new Promise 객체를 담은 변수를 콘솔창에 출력해보면요
pending, fulfilled, rejected 세가지 상태중에 하나가 출력됩니다.
const promiseObj = new Promise(function (resolve, reject) {
const tenSecondsCalc = 1 + 1;
resolve(tenSecondsCalc);
});
console.log(promiseObj);
pending - new Promise 객체의 콜백함수에 있는 코드가 아직 연산중이거나 실패,성공 판정이 나기 전 상태입니다.
fulfilled - 성공판정이 난 후 상태
rejected - 실패판정이 난 후 상태
그리고 프로미스는 아까 얘기했듯이 프로미스안에 코드 넣으면 뭐 비동기적으로 처리해주고 이런거 없습니다.
프로미스안에 10초 걸리는 연산 넣어놓으면 10초동안 브라우저 멈춥니다.
이런거도 잘 참고하시면 될거 같습니다 ~
async await
나는 콜백함수도 싫고 프로미스도 어려워서 싫다
그러면 ES8 에서 새로 추가된 async await 도 있습니다.
promise와 then 을 아주 쉽게 만들어주는 문법입니다.
async 키워드는 함수 앞에다가 쓸수 있는 키워드인데 얘를 쓰면 promise 객체가 저절로 생성됩니다.
async function tenSecondCalc() {
return 1 + 1;
}
이렇게 함수앞에 async 붙이면 tenSecondCalc 함수 자체가 promise 가 됩니다.
그래서 이 함수 실행할때 뒤에 then 붙일 수 있습니다. promise 니깐요.
(함수 실행하면 그자리에 Promise 인스턴스가 남음)
async function tenSecondCalc() {
1 + 1;
}
tenSecondCalc().then(() => {
console.log('어려운 연산 성공');
});
async 함수 안에서 연산한 결과를 받고싶으면 return 을 해주고 then 에서 받아준다.
async function tenSecondCalc() {
return 1 + 1;
}
tenSecondCalc().then((result) => {
console.log(result); // 2
});
then 이 싫다면 드디어 await 가 나올 차례입니다.
await 는 그냥 프로미스.then() 의 대체품인데 문법이 아주 쉽습니다.
async function onePlusOne() {
const tenSecondCalc = new Promise((resolve, reject) => {
const result = 1 + 1;
resolve(result);
});
const awaitResult = await tenSecondCalc; // ------*
}
onePlusOne();
정확한 뜻은
"tenSecondCalc 라는 promise 를 기다린 다음 완료되면 결과를 awaitResult 변수에 담아주세요~ "
라는 뜻임.
근데 tenSecondCalc 라는 프로미스 안에 비동기식 요청이 들어있으면 브라우저가 await 기다리는동안 잠깐 멈추게 됨.
주의하십시오 휴먼.
tenSecondCalc 프로미스가 실패하면 await tenSecondCalc 는 에러를 내고 코드 실행이 멈춰버립니다.
그래서 await 밑에있는 다음 코드들이 실행이 안됨.
그래서 try{} catch() {} 구문을 써야합니다.
나쁜 사람이 갑자기 onePlusOne 함수 실행해서
await tenSecondCalc 가 에러를 뿜어내기 전에 얼른 위에있는 코드 수정해줍시다
async function onePlusOne() {
const tenSecondCalc = new Promise((resolve, reject) => {
const result = 1 + 1;
resolve(result);
});
try {
const awaitResult = await tenSecondCalc;
} catch {
console.log('에러났네요');
}
}
onePlusOne();
이렇게 await 구문 쓸때는 try catch 가 세트라고 생각하면 좋습니다.
근데 await 구문이 에러가 절대 안날거라 자신있으면 try catch안해도 됌
'javascript' 카테고리의 다른 글
정규 표현식 - RegExp (0) | 2022.06.20 |
---|---|
array, object 콘솔에서 출력할 때 신기한 점 - console lazy evaluation (0) | 2022.03.25 |
js sort 함수로 정렬해버리기 (0) | 2022.03.24 |
js this (javascript this)가 가리키는 3가지 (0) | 2022.03.24 |
for문 삼대장 - for ...of , for ...in, forEach (0) | 2022.03.10 |
댓글