다른 코드의 인자로 넘겨주는 함수
콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수이다.
콜백 함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행한다.
setInterval
함수는 첫 번째 매개변수로 익명함수를 두 번째 매개변수로 숫자를 받는다.
setInterval
이라고 하는 ''다른 함수'에 익명함수를 넘겨주면 제어권도 함께 넘겨받은 setInterval이 스스로의 판단에 따라 적절한 시점에 이 익명 함수를 실행한다.
이처럼, 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수의 호출 시점에 대한 제어권을 가진다.
map
메서드는 첫 번째 인자로 callback
함수를 받고, 두 번째 인자로 콜백 함수 내부에서 this로 인식할 대상 을 특정할 수 있다. 두 번째 인자는 생략할 수 있으며 이 경우 전역객체(브라우저의 경우 윈도우 객체)를 가리킨다.
위의 사진에서 두 번째 인자로 'callback'이라는 string 값을 전달했기 때문에 this는 'callback'이 찍힌다.
this를 특정하지 않은 경우
[10, 20, 30].map(function(curr, index) {
console.log(curr, index);
console.log(this);
return curr + 5;
});
<output>
10 0
Window {...}
20 1
Window {...}
30 2
Window {...}
this를 특정한 경우
[10, 20, 30].map(function(curr, index) {
console.log(curr, index);
console.log(this);
return curr + 5;
}, 'callback');
<output>
10 0
'callback'
20 1
'callback'
30 2
'callback'
위의 사진에서 logValues는 obj 객체의 메서드로 정의됐다.
obj.logValues(1, 2)
는 이름 앞에 점이 있기 때문에 메서드로서 호출한 것이다. 따라서 this는 객체 자체 obj를 가리키고, 인자로 넘어온 1과 2가 출력된다.
setTimeout(obj.logValues, 1000)
은 메서드를 setTimeout의 콜백함수로서 전달했다. 즉, obj를 this로 하는 메서드를 그대로 전달한 것이 아니라 obj.logValues가 가리키는 함수만 전달한 것이다. 따라서 this는 전역 객체를 바라보게 된다.
{ vals: Array(3), logValues: f logValues() } [1, 2, 3] 1 2
Window { window: Window, self: Window, document: HTMLDocument, name: "" ... }
self 변수에 this를 담고, 익명함수를 선언함과 동시에 반환하는 방식이다.
이 방식은 실제로 this를 사용하지 않을 뿐더러 번거롭다.
this 자리에 객체 자체를 넣는 방식이다.
훨씬 간결하고 직관적이지만 this를 이용해 다양한 상황에 재활용할 수 없게 되었다.
위의 예시에서 우리는 obj.vals 값에 각 [ 1, 2, 3 ]과 [ 4, 5, 6 ]이 출력되길 바란다.
하지만, 실제 결과는 두 결과 값 모두 [ 1, 2, 3 ]이 나온다. 처음부터 바라볼 객체를 명시적으로 obj로 지정했기 때문에 어떤 방법으로도 다른 객체를 바라보게끔 할 수가 없다.
{ vals: Array(3), logValues: f logValues() } [ 1, 2, 3 ]
{ vals: Array(3), logValues: f logValues() } [ 1, 2, 3 ]
bind
, call
, apply
메서드는 첫 번째 매개변수에 this로 지정할 값을 넣는다.
bind
는 call
과 비슷하지만 즉시 호출하지 않고 넘겨 받은 this를 바탕으로 새로운 함수를 반환한다. apply
는 두 번째 매개변수부터 배열로 받는다.
{ vals: Array(3), logValues: f logValues() } [ 1, 2, 3 ]
동기적인 코드는 현재 실행 중인 코드가 완료된 후, 다음 코드를 실행하는 방식이다.
CPU의 계산에 의해 즉시 처리 가능한 대부분의 코드는 동기적인 코드이며 실생활 예로 은행이 동기적인 곳이다.
비동기적인 코드는 현재 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식이다.
setTimeout이나 addEventListener처럼 별도의 요청이나 실행 대기, 보류와 관련된 코드는 비동기 코드이다.
위의 사진은 콜백 지옥의 예시이다. 목적 달성에는 지장이 없고 짧은 코드라 덜 복잡해보이지만, 코드가 길어질 수록 들여쓰기 수준이 과도하게 깊어질 것이다.
오랜 시간동안 자바스크립트는 비동기적인 작업을 동기적으로 보여주기 위해 노력해왔고, 그 대표적인 방법이 ES6의 Promise
와 ES2017의 async/await
이다.
new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만 그 내부에 resolve 또는 reject 함수를 호출하는 구문이 있을 경우, 둘 중 하나가 실행되기 전까지는 다음(then) 또는 오류 구문(catch)으로 넘어가지 않는다.
따라서 Promise 안의 작업이 완료된 후에야 다음 코드(then, catch)를 실행하는 동기적 표현이 가능하다.
function delay(n) {
let answer = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n);
}, 1000);
})
return answer;
}
delay(1).then(result => {
console.log(result);
return delay(2);
}).then(result => {
console.log(result);
return delay(3);
}).then(result => {
console.log(result);
return delay(4);
}).then(result => {
console.log(result);
return delay(5);
}).then(result => {
console.log(result);
})
ES2017에서는 가독성이 뛰어나면서도 작성법이 간단한 새로운 기능인 async/await
가 추가되었다.
비동기 작업을 수행하고자 하는 함수 앞에 async를 표기하고, 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기한다.
주로 Promise는 끝나는 시점을 알려주는 용도로, async/await는 해당 구문이 완료될 때까지 기다리는 용도로 사용된다. 아래의 코드는 반복되는 코드를 for 문으로 바꿔준 것이다.
function delay(n) {
let answer = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n);
}, 1000);
})
return answer
}
let numList = async function() {
for(let n=0; n<6; n++) {
let result = await delay(n);
console.log(result);
}
}
numList();
코어 자바스크립트(Core JavaScript)
https://book.naver.com/bookdb/book_detail.nhn?bid=15433261
좋은 정보 감사합니다. 그런데 궁금한게 있는데요 전통적인 방식(2) - 문제점에서 두번째 setTimeout() 메소드의 기대값이 [4, 5, 6] 인가여...??