익명 함수를 이벤트 리스너로 지정하고 이벤트 리스너 안에서 함수를 실행하면 그 함수에 추가적인 정보를 값으로 넘길 수 있다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="box">click me</div>
<script>
window.onload = function () {
var box = document.getElementById("box");
box.addEventListener("click", changeBgColor("red"), false);
function changeBgColor(color) {
return function (e) {
e.currentTarget.style.backgroundColor = color;
};
}
};
</script>
</body>
</html>
객체와 객체 사이의 관계를 느슨하게 연결하는 수단
click이나 mousedown등의 표준 이벤트 외에 독자적인 이벤트를 생성할 수 있다.
createEvent
메서드로 이벤트 객체를 생성 -> dispatchEvent
메서드를 호출해서 이벤트를 보냄
코드에서 type
은 생성할 이벤트 객에의 이벤트 타입을 뜻한다.
var event = document.createEvent(type);
생성된 이벤트 객체는 해당 이벤트의 타입에 따라 초기화 작업을 해야 한다.
event.initEvent(type, bubbles, cancelable);
// type : 이벤트 유형을 뜻하는 문자열("click", "mouseup" 등)
// bubbles : 버블링할지를 나타내는 논리값
// 버블링 설명 : https://ko.javascript.info/bubbling-and-capturing
// cancelable : 취소할 수 있는 이벤트로 만들지를 나타내는 논리값
초기화 작업을 끝낸 후에는 이벤트 타깃 요소를 상대로 이벤트를 보낸다. 이벤트는 요소 객체의 dispatchEvent
메서드로 보낸다.
target.dispatchEvent(event);
앞서 정의한 이벤트 객체를 사용해서 button
객체에 이벤트를 보내려면 다음과 같이 작성한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>
<button id="button">버튼</button>
</p>
<script>
window.onload = function () {
var event = document.createEvent("HTMLEvents");
event.initEvent("click", true, false);
var button = document.getElementById("button");
button.addEventListener("click", function (event) {
alert(`event.cancelable : ${event.cancelable}`);
});
button.dispatchEvent(event);
};
</script>
</body>
</html>
커스텀 이벤트는 이벤트 객체의 isTrusted 프로퍼티로 구별 가능하다. isTrusted 값이 true
면 표준이벤트, false
면 커스텀 이벤트
커스텀 이벤트의 사용 이유
객체와 객체가 통신하는 주요 수단은 인수로 콜백함수를 넘기는 것이며 또 다른 방법은 커스텀 이벤트를 보내는 것이다. 즉, 한쪽 객체에서는 이벤트 발생, 다른 한쪽 객체에서는 이벤트를 처리하는 리스너를 등록해서 처리한다.
console.log("A");
setTimeout(function () {
console.log("B");
}, 0);
console.log("C");
위 코드의 실행 결과는 A -> C -> B 순서로 콘솔에 표시된다. setTimeout
함수의 인수로 받은 콜백 함수는 시간이 흐른 후에 처리하도록 예약처리만하고 다음 코드로 넘어가기 때문이다.
다음 코드는 1초 후에 "A" 1초 후에 "B" 1초 후에 "C"를 표시하는 코드이다. sleep 함수는 callback이라는 콜백 함수를 1초 후에 실행하는 방식으로 비동기 처리를 구현했다.
function sleep(callback) {
setTimeout(function () {
callback();
}, 1000);
}
sleep(function () {
console.log("A");
sleep(function () {
console.log("B");
sleep(function () {
console.log("C");
});
});
});
// A, B, C
위 코드처럼 콜백함수를 여러 개 중첩하면 이해하기 어려워지는데 이를 콜백지옥
이라고 부른다. Promise
를 사용해서 이와 같은 문제를 해결할 수 있다.
Promise
는 비동기 처리를 하고 다음 처리를 실행하기 위한 용도로 사용된다. Promise
를 사용하려면 객체를 생성해야 한다.
var promise = new Promise(funtion(resolve, reject) {...});
// resolve : 함수 안의 처리가 끝났을 때 호출해야 하는 콜백 함수.
// reject : 함수 안의 처리가 실패했을 때 호출해야 하는 콜백 함수
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("A");
resolve();
}, 1000);)
});
promise.then(function() {
console.log("B");
})
// A, B
위의 코드에서 Promise
에 인수로 넘긴 함수는 비동기 처리를 수행하는 함수이다. 1초 후에 "A"를 표시하고, resolve
함수를 호출해서 Promise
안의 처리를 종료한다.
resolve
함수가 실행되면 then
메서드에 등록한 함수가 호출된다.
resolve
함수에 인수로 넘긴 값은 then
메서드에 인수로 넘긴 함수에 전달되어 다음 처리를 위해 사용한다.
promise.then(onFullfilled);
위 코드에서 onFullfilled
함수는 성공 콜백 함수라고 하며 promise 안의 처리가 정상적으로 끝났을때 호출된다.
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
var name = prompt("이름을 입력하세요.");
resolve(name);
}, 1000);
});
promise.then(function (name) {
console.log(`안녕하세요 ${name} 님`);
});
resolve
와 마찬가지로 reject
함수에도 값을 넘길 수 있다. then
대신 catch
메서드에 넘긴 함수가 실행된다.
promise.catch(onRejected);
위 코드에서 onRejected
함수는 실패 콜백 함수라고 하며 promise 안의 처리가 실패로 끝났을때 호출된다.
promise.then(onFullfilled, onRejected);
promise 안의 처리가 성공하면 onFullfilled
함수가 실행되고 실패하면 onRejected
함수가 실행된다.
function buyAsync(money) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
var payment = parseInt(prompt("지불하고자 하는 금액을 입력하세요"));
var balance = money - payment;
if (balance > 0) {
console.log(`${payment}원을 지불했습니다.`);
resolve(balance);
} else {
reject(`구매할 수 없습니다. 잔액은 ${money}원 입니다.`);
}
}, 1000);
});
}
buyAsync(500)
.then(function (balance) {
console.log(`잔액은 ${balance}원 입니다.`);
})
.catch(function (error) {
console.log(error);
});
function buyAsync(money) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
var payment = parseInt(prompt("지불하고자 하는 금액을 입력하세요"));
var balance = money - payment;
if (balance >= 0) {
console.log(`${payment}원을 지불했습니다.`);
resolve(balance);
} else {
reject(`구매할 수 없습니다. 잔액은 ${money}원 입니다.`);
}
}, 1000);
});
}
buyAsync(500)
.then(function (balance) {
console.log(`잔액은 ${balance}원 입니다.`);
return buyAsync(balance);
})
.then(function (balance) {
console.log(`잔액은 ${balance}원 입니다.`);
return buyAsync(balance);
})
.then(function (balance) {
console.log(`잔액은 ${balance}원 입니다.`);
return buyAsync(balance);
})
.catch(function (error) {
console.log(error);
});
Promise.all 메서드
Promise의 all 메서드는 모든 처리가 성공적으로 끝났을 때만 다음 작업을 실행하도록 한다.
Promise.all(iterable);
iterable은 반복 가능한 객체이다.
예를 들어 Promise 객체가 요소로 들어있는 배열을 넘기면
Promise.all
메서드는 그 안의 요소로 들어있는 모든 Promise 객체를 병렬로 실행한다.
resolve 호출 -> then 메서드 실행 (인수로 response라는 배열을 받음)
실패로 끝난 Promise 객체가 하나라도 있다면 가장 먼저 실패로 끝난 Promise 객체에서 실행한 reject 함수의 인수를 실패 콜백 함수에 인수로 넘긴다.
function buyAsync(name, money) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
var payment = parseInt(
prompt(`${name}님, 지불하고자 하는 금액을 입력하세요.`)
);
var balance = money - payment;
if (balance > 0) {
console.log(`${name} : ${payment}원을 지불했습니다.`);
resolve(balance);
} else {
reject(`${name} : 잔액은 ${balance}원 입니다. 구매 불가능`);
}
}, 1000);
});
}
Promise.all([
buyAsync("Tom", 500),
buyAsync("Huck", 600),
buyAsync("Becky", 1000),
])
.then(function (balance) {
console.log(balance);
})
.catch(function (error) {
console.log(error);
});
Promise.race 메서드
Promise의 race 메서드는 가장 먼저 종료한 Promise 객체의 결과만 다음 작업으로 보낸다.
function buyAsync(name, money) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
var payment = parseInt(
prompt(`${name}님, 지불하고자 하는 금액을 입력하세요.`)
);
var balance = money - payment;
if (balance > 0) {
console.log(`${name} : ${payment}원을 지불했습니다.`);
resolve(balance);
} else {
reject(`${name} : 잔액은 ${balance}원 입니다. 구매 불가능`);
}
}, 1000);
});
}
Promise.race([
buyAsync("Tom", 500),
buyAsync("Huck", 600),
buyAsync("Becky", 1000),
])
.then(function (balance) {
console.log(balance);
})
.catch(function (error) {
console.log(error);
});