[자바스크립트] 콜백함수 / 비동기 처리 / await
콜백함수
다른 함수의 인자로서 이용되는 함수 / 어떤 이벤트에 의해 호출되어지는 함수
함수를 변수로 저장할 수 있기에 변수로 저장된 함수표현식을 인수로 사용해 전달가능
함수를 나눠줘서 사용하고자 하는 코드만 사용할 수 있어서 활용도 높음 (코드 재사용)
사용원칙
1) 익명의 함수 사용
2) 함수의 이름만 넘김 : 함수를 변수 or 다른 함수의 변수처럼 사용가능
3) 전역변수 / 지역변수를 콜백함수의 파라미터로 전달
콜백 함수를 통한 비동기 처리 Asynchronous
호출부에서 실행 결과를 기다리지 않아도 되는 함수, 비순차적 처리 ( <-> 동기 함수)
브라우저에서 어떤 로직이 동기 함수만으로 실행될 경우, 기다리는 시간 증가로 불편함
비동기 함수를 사용하면 로직을 순차적으로 처리할 필요가 없어, 동시처리에 유리
function loadScript(src) {
let script =document.createElemnet('script');
script.src = src;
document.head.append(script);
}
▷ 내장 비동기 함수
1) setTimeout(실행할 작업을 담은 콜백함수, 콜백함수 수행전 초단위 시간, 넘길 인자 )
코드를 바로 실행하지 않고 일정 시간 기다린 이후에 실행해야 하는 경우 사용
세번째 인자부터는 첫번째 인자로 넘어온 함수가 인자를 받는 경우, 이 함수에 넘길 인자를 명시
function add(x,y) {
console.log(x+y);
}
setTimeout(add, 2000, 3, 4); // 7
// 두개의 수를 인자로 받아 더한 값을 출력하는 add()함수
// 인자로 3과 4를 넘겨서 2초 후에 호출
const timeout = setTimeout(() => console.log("5초 이후 실행"), 5000);
clearTimeout(timeout);
// clearTimeout을 사용하면 실행될 코드를 취소시킴
2) setInterval (실행할 작업을 담은 콜백함수, 초단위 시간)
특정 부분을 주기적으로 업데이트 하거나, API로부터 변경된 데이터를 주기적으로 받아와야 하는 경우
어떤 코드를 일정 시간 간격을 두고 반복해 실행하고 싶을 때 사용함
3) clearInterval() 코드가 주기적으로 실행되는 것을 중지
// 내장함수 - 타이머
// 특정 시간마다 함수를 실행
let id = setInterval(
// 익명함수로 감싸서 넣어야지만 clearInterval을 실행할 수 있음
()=> documet.write("<p>안녕하세요</p>"),2000
)
// id값을 통해서 특정 시간에 멈추기
setTimeout(
// 콜백 함수를 넣어야 하기 때문에, 익명 함수 or 함수의 이름을 넣어야 함
// 함수이름(); 을 하면 함수가 바로 호출되어 실행
clearInterval(id),10000
)
→ 내장 비동기 함수를 사용한 이후에, clearTimeout() 과 clearInterval()을 사용해 타이머 청소를 해, 메모리 누수에 주의!
비동기 처리의 단점
예상하지 못한 순서로 코드가 실행될 때,
함수로부터 결과값을 리턴 받기를 포기하고, 결과값을 이용해 처리할 로직을 콜백함수에 담아 인자로 던지기
But, 콜백함수를 중첩해 사용하는 경우
코드 들여쓰기의 연타와 코드 가독성의 저하 -> 콜백 지옥 -> Promise or async /await을 사용
1) Promise
현재는 얻을 수 없지만(지연시간 발생) 가까운 미래에는 얻을 수 있는 데이터에 접근하기 위한 방법
promise를 사용하면 동기 메서드처럼 일정 시점 이후에 결과를 제공할 수 있다는 promise를 반환
e.g. 지연 발생 : I/O or Network를 통해 데이터를 얻는 경우
실행 코드 입장에서는 긴 지연시간으로 여겨지기 때문에 자바스크립트(non-blocking)에서는 비동기 처리 필수
▷ try-catch 방식과 유사한 방법으로 비동기 처리 코드 작성
then() 메소드의 결과값을 가지고 수행할 로직을 담은 콜백함수
catch() 메서드는 예외 처리 로직을 담은 콜백 함수
++ 또 다른 promise 객체를 리턴할 수 있는데,
이 객체를 then/catch메서드를 통해 접근 이 두 메서드는 사슬처럼 연결해 연쇄호출 가능
const promise = new Promise(function(resolve, reject) {...});
// 화살표 함수 형식
function returnPromise() {
return new Promise((resolve, reject) => {...});}
// resolve() : 미래시점 결과
// reject() : 미래시점 발생 예외
// 나눗셈 비동기 처리
function devide(n1, n2) {
return new Promise((resolve, reject) => {
if(n2 === 0) reject(new Error("0으로 나눌 수 없음"));
else resolve(n1 / n2);
});
}
// 정상적인 인자 넣기
devide(4,2)
.then((result)=>console.log("성공:", result)) // 성공 :2
.catch((error)=>console.log("실패:", error));
// 비정상적인 인자 넣기
devide(8, 0)
.then((result) => console.log("성공:", result))
.catch((error) => console.log("실패:", error));
// 실패 : Error : 0으로 나눌 수 없음
2) async / await
Async 함수 객체를 반환하는 하나의 비동기 함수 -> async 함수는 항상 promise를 반환
1) 첫번째 await 문이 없는 async함수는 동기적으로 실행
2) await문이 있다면 async함수는 항상 비동기적으로 완료
async function name([param[,param[, ... param]]]){
// function 앞에 위치
// 해당 함수는 항상 promise를 반환
statements
}
// name = 함수의 이름
// param = 함수에게 전달되기 위한 인자 이름
// statements = 함수 본문 구성 내용 ( await)
// await는 async 함수 안에서만 동작합니다.
let value = await promise;
await 은 async함수에서나 사용가능하며, promise가 처리되고 난 이후에 반환
promise가 처리되길 기다리는 동안 엔진이 다른 일을 할 수 있기 때문에 CPU리소스가 낭비되지 않음
→ 에러가 발생할 경우 : 예외 생성
→ 에러 미발생 : promise객체의 result 값을 반환
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});
let result = await promise; // 프라미스가 이행될 때 까지 기다림
alert(result); // 완료!
}
f();
참고자료
1) async/await : https://ko.javascript.info/async-await
2) async function : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function