21) 구조분해할당, 클로저, 프로미스, async, await

ppparkta·2022년 12월 26일
1

웹개발

목록 보기
22/26

요약

  • JS
    • 구조분해할당
    • 클로저
    • 프로미스(callback, promise, then, catch, finaly)
    • async, await

JS

구조분해할당

배열, 객체등의 오브젝트의 값을 바로 할당하는 방법이다.

배열 구조분해할당

const c = [1, 2];
const [a, b] = c;
// a=1, b=2

기본적인 사용방법은 위와 같다.

초기값을 주고싶을 때 다음과 같이 사용할 수 있다.

const a = [1, 2];
const [b = 0, c = 0, d = 3] = a;
//b=1, c=2, d=3

다음과 같이 초기값을 설정하고 구조분해할당하면 배열에 값이 없는 경우 초기값으로 값이 할당된다. 그러나 배열 안에 값이 있다면 배열 안의 값을 우선으로 할당한다.

일부 자료를 건너뛰고 싶을 때는 다음과 같이 설정할 수 있다.

const a = [1, 2, 3];
const [b, , c] = a;
// b=1, c=3

객체 구조분해할당

객체 구조분해할당도 배열 구조분해할당과 유사하다. 다른 점은 배열과는 달리 할당받는 순서가 달라져도 괜찮다.

let user  = {name: 'Mike', age: '23',};
let {name, age} = user;
// name='Mike', age='23'
let user  = {name: 'Mike', age: '23',};
let {age, name} = user;
// age='23', name='Mike'

원래 객체의 이름으로 변수를 사용해야 하는 것은 안디ㅏ. 새로운 변수 이름으로 할당하는 것도 가능하다.

let user  = {name: 'Mike', age: '23',};
let {name: userName, age: userAge} = user;
// userAge='23', userName='Mike'

객체 구조분해할당도 초기값을 설정할 수 있다. 규칙은 배열 구조분해할당과 같다. 할당받은 값이 undifined일 때만 초기값으로 할당된다.

클로저(closure)

자바스크립트는 어휘적 환경(Lexical Environment)에서 실행된다.

클로저란 함수와 어휘적 환경의 조합을 의미한다. 함수가 생성될 당시의 외부 변수를 기억하고 생성한 이후에도 계속 접근할 수 있다.

이것을 이해하기 위해서 어휘적 환경을 알아야 한다. 어휘적 환경이란 const, let, var 등이 실행 초기 단계에 선언되고 초기화되어 사용 가능해지는 원리가 되는 환경이다. (호이스팅, TDZ 관련)

개인적으로 이해한 내용은, 하나의 함수를 여러 변수에 할당해서 사용할 때 각각의 변수로 초기화된 함수는 서로 다른 공간에서 사용되는 별개의 것임. 영향을 주지 않는다.

프로미스(promise)

정리되지 않은 느낌이다. 어떤 상황에서 쓰는지 찾아보고 설명 다시 보자!

음식점을 예로 들면, 어떤 음식을 주문하고 음식이 나왔는지 확인하기 위해서 소비자가 10분에 한번씩 음식이 나왔는지 음식점에 물어볼 수 있다.

이 때 음식점은 음식이 나오기 전까지 계속 나오지 않았다는 응답을 주다가 음식이 나오면 소비자가 물어보러 올 때 준비됐다고 응답한다.

그러나 이런 방식은 언제 음식이 준비될지 모르기 때문에 비효율적이다. 그것보다는 소비자가 음식점에 음식이 준비됐을 때 전화를 달라고 하고 전화번호를 남기고 가는 편이 더 효율적일 것이다.

이럴 때 사용할 수 있는 것이 프로미스다.

const pr = new Promise((resolve, reject) => {
	// code
});

callback

Promise라는 함수를 이용해서 요청하는데, resolve는 성공했을 때 실행되는 함수이고, reject는 실패했을 때 실행되는 함수이다.

어떤 일이 완료되고 실행되는 함수를 callback함수라고 한다.

프로미스 함수가 실행되면 초기 상태는 state: pending, result: undefined 을 갖는다.

여기서 어떤 일이 성공하면 resolve(value) 가 실행되며 프로미스 함수의 상태도 바뀌게 된다. (state: fulfilled, result: value)

어떤 일이 실패하면 reject(error) 가 실행되며 프로미스 함수의 상태가 바뀌게 된다. (state: rejected, result: error)

const pr = new Promise((resolve, reject) =>{
	setTimeout(() => {
    	resolve('OK');
    }, 3000)
});

then

지금까지의 상태 변화는 음식점 입장에서 서술한 것이다. 소비자 입장에서 상태 변화는 다음과 같이 표현할 수 있다.

pr.then(
    //이행됐을 때 실행
	function(result){}, 
    //거부됐을 때 실행
  	function(err){} 
);

조금더 디테일한 예시는 아래와 같다.

pr.then(
    //이행됐을 때 실행
	function(result){
    	console.log(result + ' 가지러 가자.');
    }, 
    //거부됐을 때 실행
  	function(err){
   		console.log('다시 주문해주세요.');
    } 
);

catch

then 이외에 실행할 있는 것이 catch이다. catch는 오류가 생겼을 때 실행되는 문장을 표시할 수 있다. 따라서 error를 then이 아니라 catch에 넣어주는 것이 가독성을 더 높일 수 있다.

pr.then(
	function(result){}  
).catch(
	function(err){}
)

finaly

finaly는 promise의 이행이든, 거부든 마지막에 항상 실행된다.

pr.then(
	function(result){}  
).catch(
	function(err){}
).finaly(
	function(){
    	console.log('--- 주문 끝 ---')
    }
)

프로미스 체이닝

전체적인 사용 예시를 보면서 감을 익혀보는 것이 좋을거같다.

여러 개의 프로미스가 연결, 연결, 연결되는 묶음을 프로미스 체이닝이라고 부른다.

const f1 = () => {
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("1번 주문 완료!");
        }, 1000);
    });
};


const f2 = (message) => {
  	console.log(message);
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("2번 주문 완료!");
        }, 3000);
    });
};

const f3 = (message) => {
  	console.log(message);
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("3번 주문 완료!");
        }, 2000);
    });
};

console.log("시작");
f1()
	.then((res) => f2(res))
	.then((res) => f3(res))
	.then((res) => console.log(res));
//시작
//1번 주문 완료
//2번 주문 완료
//3번 주문 완료

만약 중간에 f2함수가 rej를 실행하면 then으로 연결된 뒤의 f3 함수의 프로미스는 실행되지 않는다. 서로 연결관계가 있기 때문에 이런 것을 프로미스 체이닝이라고 이름짓는 것이다.

promise all

위의 프로미스 체이닝 예제에 쓰인 코드는 실행에 총 6초가 필요하다. 그러나 모든 promise를 한번에 받은 다음에 처리되는대로 응답하면 실행에 3초만 필요하다.

모든 promise를 한번에 받기 위해 promise all을 사용한다. 함수를 배열로 받는다.

Promise.all([f1(), f2(), f3()])
	.then((res) => {
		console.log(res);
	});

promise all은 하나의 정보라도 rej되면 모든 정보를 가져오지 않는다. 따라서 전부 보여주거나 전부 감춰야 할 때 사용한다.

promise race

promise all은 모든 작업이 완료될 때까지 기다리지만 promise race는 하나라도 완료되면 작업을 끝낸다. 이름 그대로 레이스의 성질을 갖고있다.

async

async, await 함수를 호출하면 promise의 then을 체인 형식으로 호출하는 것보다 가독성이 좋아진다.

async function getName(){
	return "Mike";
}

어떤 함수에 async 키워드를 붙이면 항상 promise를 반환한다. 그래서 이렇게 호출한 함수에는 then을 사용할 수 있다.

async function getName(){
	return "Mike";
}

getName().then(name => {
	console.log(name);
});

만약에 getName함수의 반환값이 Promise라면 반환값을 그대로 사용한다. 그렇지 않으면 promise를 생성해서 반환한다.

getName함수에 예외가 발생하면 reject를 반환할 수 있다.

async function getName(){
	// return "Mike";
  	throw new Error("err...);
}

getName().catch((err) => {
	console.log(err);
});

await

await키워드는 async함수 내부에서만 사용할 수 있다. 일반 함수 안에서 사용하면 에러가 발생한다.

await키워드를 사용하면 promise함수가 반환될 때까지 기다린 뒤에 값을 반환한다.

function getName(name){
	return new Promise((resolve, reject) => {
      setTimeout(() => {
      	resolve(name);
      }, 1000);
    });
}

async function shwoName(){
	const result = await getName("Mike");
  	console.log(result);
}

console.log("시작");
showName();

아까 promise/then을 정리하며 썼던 코드를 async/await으로 변경하면 아래와 같다.

const f1 = () => {
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("1번 주문 완료!");
        }, 1000);
    });
};


const f2 = (message) => {
  	console.log(message);
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("2번 주문 완료!");
        }, 3000);
    });
};

const f3 = (message) => {
  	console.log(message);
	return new Promise((res, rej) => {
    	setTimeout(() => {
        	res("3번 주문 완료!");
        }, 2000);
    });
};

console.log("시작");
async function order(){
	const result1 = await f1();
  	const result2 = await f2(result1);
  	const result3 = await f3(result2);
  	console.log(result3);
  	console.log("종료");
};
order();

promise/then을 사용하는 것과 같은 코드인데 더 직관적으로 표현할 수 있다. reject를 처리하기 위해서 함수 안을 try/catch문으로 감싸주면 된다.

async function order(){
  try {
	const result1 = await f1();
  	const result2 = await f2(result1);
  	const result3 = await f3(result2);
  	console.log(result3);
  } catch(e) {
  	console.log(e);
  }
  	console.log("종료");
};

promise all처럼 사용하기 위해서는 아래와 같이 만들면 된다.

async function order(){
  try {
    const result = await Promise.all([f1(), f2(), f3()]);
  	console.log(result);
  } catch(e) {
  	console.log(e);
  }
  	console.log("종료");
};
profile
겉촉속촉

0개의 댓글