[학습 목표]
자바스크립트의 콜백함수의 개념과 예시에 대해 학습해요.
어떤 항목에서 콜백함수를 전달받은 함수에게 제어권이 이양되는지를 이해하고 그 예시를 테스트해볼 수 있습니다.
비동기적인 코드 작성에서 발생할 수 있는 콜백지옥을 예방하고 비동기제어를 할 수 있는 Promise, async/await 등의 기술을 이해하고 활용하는 능력을 배양합니다
1. 콜백함수
// 콜백함수 예시 setTimeout, 배열에 대한 forEach
// setTimeout
setTimeout(function() {
console.log("Hello, world!");
}, 1000);
// forEach
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function(number) {
console.log(number);
});
이처럼 다른 코드의 인자로 넘겨주는 함수인 콜백함수의 여러가지 특징과 바람직하지 않은 상황(콜백 지옥)을 만났을 때 어떻게 대처해야 할지 배워봅시다.
동기 vs 비동기의 개념도 학습합니다.
비돈기 적인 것을 동기적으로 보이도록 만드는 과정이 필요합니다.
(1) 콜백함수란
callback = call(부르다) + back(되돌아오다) = 되돌아와서 호출
forEach, setTimeout 등
콜백 함수는 다른 함수에 인자로 넘겨주는 함수로, 넘겨받은 함수는 적절한 시점에 이를 실행합니다. 즉, 제어권★을 넘겨받은 함수는 그에 맞는 로직을 실행하며, 콜백 함수는 제어권을 위임받은 코드가 결정된 시점에 실행됩니다.
제어권은 콜백함수가 제어권을 위임한 함수에게 있습니다.
제어권을 넘겨줄 테니 네가 아는 로직으로 처리해
(2) 제어권
◆ 호출 시점
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가집니다.
var count = 0;
// timer : 콜백 내부에서 사용할 수 있는 '어떤 게 돌고있는지' 알려주는 id값
// setInterval : 반복해서 매개변수로 받은 콜백함수의 로직을 수행
var timer = setInterval(function() {
console.log(count);
if(++count > 4) clearInterval(timer); // clearInterval() : ()안의 변수를 없애라 -> 로직을 빠져나옴
}, 300);
/*
0
1
2
3
4 */
var count = 0;
var cbFunc = function () {
console.log(count);
if (++count > 4) clearInterval(timer);
};
// setInterval이 콜백 함수의 제어권을 넘겨받은 코드입니다.
var timer = setInterval(cbFunc, 300);
// 실행 결과
// 0 (0.3sec)
// 1 (0.6sec)
// 2 (0.9sec)
// 3 (1.2sec)
// 4 (1.5sec)
→ 원래 cbFunc()를 수행한다면 그 호출주체와 제어권은 모두 사용자가 됩니다.
→ setInterval로 넘겨주게 되면 그 호출주체와 제어권은 모두 setInterval이 됩니다.
◆ 인자
map 함수 : 배열에 대한 매소드. 기존 배열을 변경하지 않고, 새로운 배열을 생성합니다.
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
var newArr = [10, 20, 30].map(function (currentValue, index) {
console.log(currentValue, index);
return currentValue + 5;
});
console.log(newArr);
// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [ 15, 25, 35 ]
콜백함수에서 여러분이 넣은 currentValue, index 이 변수의 순서를 바꾸면 ?
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담습니다.
// index, currentValue는 js의 명령어가 아닙니다. 의도한 결과가 나오지 않았습니다.
// 매게변수 자리 주의하세요
var newArr2 = [10, 20, 30].map(function (index, currentValue) {
console.log(index, currentValue);
return currentValue + 5;
});
console.log(newArr2);
// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [ 5, 6, 7 ]
의도하지 않은 값이 나옵니다.
컴퓨터는 사람이 아니기 때문에, index - currentValue의 의미를 사람처럼 이해할 수 없습니다.
이처럼, map 메서드를 호출해서 원하는 배열을 얻고자 한다면 정의된 규칙대로 작성하세요.(콜백 내부의 인자도 포함) 이 모든것은 전적으로 map 메서드. 즉, 콜백 함수를 넘겨받은 코드에게 그 제어권이 있습니다. 인자(의 순서)까지도 제어권이 그에게 있습니다..
제어권이 넘어갈 map 함수의 규칙에 맞게 ‘나는’ 호출해야 합니다!
◆ this ★
앞전 시간에서 우리는 콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조한다고 배웠습니다.
예외사항도 있었습니다.
제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
내부적으로 별도의 this를 지정하는 방식과 제어권에 대한 이해를 높이기 위해서 map함수를 직접 구현해 봅시다.
핵심은 call, apply
// Array.prototype.map을 직접 구현해봤어요!
Array.prototype.mapaaa = function (callback, thisArg) {
var mappedArr = [];
for (var i = 0; i < this.length; i++) {
// call의 첫 번째 인자는 thisArg가 존재하는 경우는 그 객체, 없으면 전역객체
// call의 두 번째 인자는 this가 배열일 것(호출의 주체가 배열)이므로,
// i번째 요소를 넣어서 인자로 전달
var mappedValue = callback.call(thisArg || global, this[i]); // map함수는 항상 callback을 받습니다.
mappedArr[i] = mappedValue;
}
return mappedArr;
};
const a = [1, 2, 3].mapaaa((item) => {
return item * 2;
});
console.log(a);
바로 제어권을 넘겨받을 코드에서 call/apply 메서드의 첫 번째 인자에서 콜백 함수 내부에서 사용될 this를 명시적으로 binding 하기 때문에 this에 다른 값이 담길 수 있다.
//이젠 이 코드를 좀 더 잘 이해할 수 있어요!!
// ★setTimeout은 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째 인자에
// 전역객체를 넘겨요
// 따라서 콜백 함수 내부에서의 this가 전역객체를 가리켜요
setTimeout(function() { console.log(this); }, 300); // Window { ... }
// ★forEach도 마찬가지로, 콜백 뒷 부분에 this를 명시해주지 않으면 전역객체를 넘겨요!
// 만약 명시한다면 해당 객체를 넘기긴 해요!
[1, 2, 3, 4, 5].forEach(function (x) {
console.log(this); // Window { ... }
});
//★addEventListener는 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째
//인자에 addEventListener메서드의 this를 그대로 넘겨주도록 정의돼 있어요(상속)
document.body.innerHTML += '<button id="a">클릭</button';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
(3) 콜백 함수는 함수다
콜백 함수로 어떤 객체의 메서드를 전달하더라도, 그 메서드는 메서드가 아닌 함수로 호출합니다.
var obj = {
vals: [1, 2, 3],
logValues: function(v, i) {
console.log(this, v, i);
}
};
//method로써 호출
obj.logValues(1, 2);
//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)
[4, 5, 6].forEach(obj.logValues);
//4, 5, 6].forEach(obj.logValues(1,4));처럼 매게변수까지 넣으면 매게변수로 넣는게 함수가 아니라 함수를 실행한 걸과를 넣게 됩니다.
// 콜백한수를 넣는건 함수 그 자체를 넣어야 합니아.
// 첫번째 인자는 기준이 되는 배열의 n번째 요소가 들어가고 그 요소의 인덱스를 매게변수로 잡습니다.
// forEack, map, filter 모두 동일.
[44, 5, 6].forEach(obj.logValues(1,4));처럼 매게변수까지 넣으면 매게변수로 넣는게 함수가 아니라 함수를 실행한 결과를 얻게 됩니다.
콜백함수에 넣는건 함수 그 자체를 넣어야 합니다.
첫번째 인자는 기준이 되는 배열의 n번째 요소가 들어가고 그 요소의 인덱스를 매개변수로 잡습니다.
forEack, map, filter 모두 동일.
(4) 콜백 함수 내부의 this에 다른 값 바인딩하기
콜백 함수 내부의 this에 다른 문맥에 맞는 객체값을 바인딩하는 방법
<전통적 방식>
우리가 이전에 강제로 this를 제어하는 방법에서 살짝 다뤘던 방식입니다.
var obj1 = {
name: 'obj1',
func: function() {
var self = this; //이 부분!
return function () {
console.log(self.name);
};
}
};
// 단순히 함수만 전달한 것이기 때문에, obj1 객체와는 상관이 없어요.
// 메서드가 아닌 함수로서 호출한 것과 동일하죠.
var callback = obj1.func();
setTimeout(callback, 1000);
var obj1 = {
name: "obj1",
// closure 방식
// : 현재의 함수가 끝났음에도 불구하고 영향력을 끼친다.
// ex) return에서 참조하는 self에서 출력되는걸 보면 밖에있는 self가 살아있음을 알 수 있다.
// func속성은 함수를 값으로 가집니다.
// 그 함수도 함수를 return하겠금 만들었습니다.
func: function () {
var self = this; //this를 self 에 할당해서 출력
return function () {
console.log(self.name);
};
},
};
// 단순히 함수만 전달한 것이기 때문에, obj1 객체와는 상관이 없어요.
// 메서드가 아닌 함수로서 호출한 것과 동일합니다.
var callback = obj1.func(); // callback 이라는 변수를 선언하고 obj1.func를 실행한 결과를 담았습니다.
/*
var callback = function() {
var self = this;
console.log(self.name);
}*/
// 실행결과가 아니라 함수를 담으려면 'var callback = obj1.func'이렇게 적었어야 합니다.
setTimeout(callback, 1000);
/*
setTimeout(function() {
var self = this;
console.log(self.name);
}, 1000);*/
실제로는 this를 사용하는게 아니기도 하고, 번거롭네요.
그렇다면, 콜백 함수 내부에서 아예 this를 사용하지 않는다면?
var obj1 = {
name: 'obj1',
func: function () {
console.log(obj1.name);
}
};
setTimeout(obj1.func, 1000);
완전 하드코딩이 됩니다... this의 이점을 사용하지도 못합니다.
첫 번째 예시 재활용
this를 우회적으로나마 활용하여 원하는 객체를 바라보게만들기
var obj1 = {
name: "obj1",
func: function () {
var self = this; //이 부분!
// this를 self에 할당하고
return function () {
console.log(self.name); // 그 self를 return 하는
// 그 sel에서 name을 찍어서 console.log하는 그함수를 리턴하합니다.
};
},
};
// ---------------------------------
// 개발하다가 로직이 이해가 안되면 직접 로직을 대입해보세요.
// obj1의 func를 직접 아래에 대입해보면 조금 더 이해가 쉽습니다!
var obj2 = {
name: "obj2",
func: function () {
var self = this;
return function () {
console.log(self.name); // 결과
};
},
};
// 역시, obj1의 func를 직접 아래에 대입해보면 조금 더 보기 쉽습니다!
// obj2와 다르게 name만 가지고 있고
// 즉시 실행되는 callback함수를 씁니다.
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);// obj3를 this바인딩하러 넘기면
// 윗줄(name : "obj3")에서 바로 바인딩 됩니다.
setTimeout(callback3, 2000);
// //var callback2 = obj2.func(); // obj2.func를 실행한 결과
// var callback2 = function () {console.log("obj2"); // 결과
// };
// setTimeout(callback2, 1500);// callback2를 1.5초 후에 실행합니다.
<가장 좋은 방법 → bind메서드의 활용>
bind메서드 : this를 바인딩 해서 새로운 함수를 반환합니다.
함수를 만들 때 유용합니다.
var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
// 방법1
// 첫번째 인수로 함수를 넣주고 두번째 인자로 초를 넣어주세요
// 첫번째 인수 함수에 바인드로 새로운 함수 리턴해줌
// setTimeout(function(){}, 1000);
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정됩니다
setTimeout(obj1.func.bind(obj1), 1000);
// 방법2
//함수 자체를 obj2에 바인딩
var obj2 = { name: 'obj2' };
//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);
(5) 콜백 지옥과 비동기 제어
◆ 콜백지옥
▷ 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 헬 수준인 경우를 말합니다.
(콜백 함수는 익명함수 입니다.)
▷ 주로 이벤트 처리 및 서버 통신과 같은 비동기적 작업을 수행할 때 발생합니다.
▷ 가독성과 유지보수측면에서 힘이 듭니다.
(출처 : https://preiner.medium.com/callback지옥에-promise-적용하기-d02272ecbabe)
◆ 동기 vs 비동기
동기와 비동기의 개념
↓동기적
↑ 비동기적
출처 : https://smallzoodevs-organization.gitbook.io/copy-of-javascript-study/day-05./1.
▷ 동기 : synchronous
- 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식.
- CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드입니다.
- 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드 역시도 동기적 코드입니다.
▷ 비동기 : a + synchronous ⇒ async라고들 흔히 부르죠
- 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
- ★ setTimeout, addEventListner 등
- 별도의 ★ 요청, 실행 대기, 보류★ 등과 관련된 코드는 모두 비동기적 코드
(출처 : https://velog.io/@mrbartrns/til-16-asynchronous-of-js)
★ 웹의 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어납니다
- 비동기작업의 특징 :
1. 순서를 보장하지 않는다.
언제 결과가, 제어권이 나에게 돌아올 지 모릅니다.
- 비동기작업을 동기적으로 사용할 수 있는 방법이 필요합니다.
순서를 보장하지 않지만 순서를 보장하는 것 처럼 보일 필요가 있습니다.
◆ 콜백지옥의 예시와 해결방안
setTimeout 콜백 지옥 예시
들여쓰기 수준 📉
값 전달 순서 : 아래 → 위
setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout( // 음료이름 찍히고 0.5초 이후에 내부 실행
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
},
500,
"카페라떼"
);
},
500,
"카페모카"
);
},
500,
"아메리카노"
);
},
500,
"에스프레소"
);
▷ 해결방법 1 [기명함수로 변환]
함수의 이름을 왼쪽에 붙여줍니다. 물고 물리면서 결국 끝까지 수행합니다.
/*해결방법1 : 기명함수로 변환
함수의 이름을 왼쪽에 붙여줍니다.
물고 물리면서 결국 끝까지 수행합니다.*/
// 장점 : 가독성 좋음
// 단점 : 익면함수 쓰면 되는데 모든 함수에 이름을 붙여야 합니다.
// 인적 리소스 낭비
var coffeeList = '';
var addEspresso = function (name) {
coffeeList = name;
console.log(coffeeList);
setTimeout(addAmericano, 500, '아메리카노');
};
var addAmericano = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addMocha, 500, '카페모카');
};
var addMocha = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(addLatte, 500, '카페라떼');
};
var addLatte = function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
};
setTimeout(addEspresso, 500, '에스프레소');
★ 비동기 작업의 동기적 표현이 필요합니다. ★
<비동기 작업의 동기적 표현(1) - Promise(1)>
★ Promise, Generator(ES6), async/await(ES7) 등
◆ Promise : 비동기 작업을 동기적 흐름처럼 표현할 수 있도록 하는 기능입니다.
★ Promise는 비동기 작업이 완료되면 알려달라는 '약속'이라고 볼 수 있습니다.
★ new연산자로 호출한 Promise의 인자로 넘어가는 콜백은 즉시 실행되며,
★ 내부에서 resolve(성공) 또는 reject(실패) 함수를 호출하기 전까지는 다음 작업(예: .then, .catch)으로 넘어가지 않습니다.
★ 비동기 작업이 완료될 때 resolve(성공) 또는 reject(실패) 함수를 호출합니다.
이를 통해 비동기 작업이 끝나기를 기다렸다가 특정 코드가 실행되도록 할 수 있어, 비동기 작업을 더 직관적이고 동기적인 표현으로 구현할 수 있게 됩니다.
new Promise(function (resolve) {
setTimeout(function () { // 콜백함수는 바로 실행.
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 아메리카노';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페모카';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페라떼';
console.log(name);
resolve(name);
}, 500);
});
});
<비동기 작업의 동기적 표현(2) - Promise(2)>
직전 예제의 반복부분을 함수화 한 코드에요. trigger를 걸어주기 위해 클로저 개념이 나왔지만, 여기서는 skip 하고, 다음 chapter에서 다루게 될거에요!
/**
* refactoring : 비 효율적인 코드를 효율적인 구조로 재구성시키는 작업
* re 다시
* factoring 코드를 재구성하거나 구조화하는 작업
*/
// var addCoffee = function (name) {
// 화살표 함수 이용 가능
var addCoffee = (name) => {
return function (prevName) {
// %%%% 다음으로 실행되는 코드들%%%%
// 이 안에서도 새롭게 Promise를 만듭니다.
return new Promise(function (resolve) {
// ***처음 실행함수*****
setTimeout(function () {
//var newName = prevName + ', ' + name;
// 백틱으로 문자열 지정 `내용${변수}내용`
// var newName = `${prevName}, ${name}`; // 처음 실행 시 prevName 없음.
var newName = prevName ? prevName + ", " + name : name;
console.log(newName);
resolve(newName);
}, 500); // ****여기까지 처음실행함수**** // %%%% 다음으로 실행되는 코드들%%%%
});
};
};
addCoffee("에스프레소")() // return 받으려고 () 붙여줌
.then(addCoffee("아메리카노"))
.then(addCoffee("카페모카"))
.then(addCoffee("카페라떼"));
<비동기 작업의 동기적 표현(3) - Generator>
◆ 제너레이터 문법
▷ 제너레이터 함수는 *가 붙은 함수로, 완료시 Iterator 객체를 반환합니다.
제너레이터 함수는 반복할 수 있는 Iterator객체를 생성합니다.
▷ Iterator 객체는 next() 메서드를 사용해 순환할 수 있습니다.
◆ 이터러블 객체(Iterable)
▷ iterator : 스페인어로 형용사 반복될 수 있는, 반복할 수 있는
▷ next() 메서드를 호출할 때마다 제너레이터 함수 내의 yield 키워드가 있는 부분까지 실행합니다.
▷ 이후 next()를 다시 호출하면, 멈췄던 위치에서부터 다음 yield(양보하다, 미루다)까지 실행됩니다.
순서를 기다리지 않는 비동기적 작업을 좀더 미루고 양보하게 만들기 위해 사용합니다
◆ 비동기 작업과 Generator
▷★비동기 작업이 완료될 때마다 next() 메서드를 호출하면, Generator 함수는 코드가 위에서 아래로 순차적으로 진행됩니다.
/**
*★ 비동기적 코드를 동기적으로 보이게 만드는 이유 ★
* 순서를 보장할 수 없으니 순서 보장이 필요한 로직에서 순서를 보장받기 위해서 필요합니다.
*
* Generator함수가 완료되면 Iterator 객체를 반환합니다.
* = coffeeMaker가 Iterator 객체를 가지고 있다는 뜻입니다.
*
* **/
var addCoffee = function (prevName, name) { // addCoffee안에next가 있어서
setTimeout(function () {
coffeeMaker.next(prevName ? prevName + ', ' + name : name);
}, 500);
};
var coffeeGenerator = function* () { // *가 있는 function*는 Generator함수입니다.
var espresso = yield addCoffee('', '에스프레소');
console.log(espresso);
var americano = yield addCoffee(espresso, '아메리카노');
console.log(americano);
var mocha = yield addCoffee(americano, '카페모카');
console.log(mocha);
var latte = yield addCoffee(mocha, '카페라떼');
console.log(latte);
};
/*
* Generator함수가 완료되면 Iterator 객체를 반환합니다.
* = coffeeMaker가 Iterator 객체를 가지고 있다는 뜻입니다.
* Iterator 객체는 계속해서 순환할 수 있는 객체 입니다.
yield키워드를 만나면 멈춥니다. yield가 있는 줄이 끝날 때 까지 기다려 주고
next를 호출하면 다시 멈췄던 부분으로 갑니다.
이유 : addCoffee안에next가 있기 때문입니다.
*/
var coffeeMaker = coffeeGenerator(); // coffeeMaker가 Iterator 객체
coffeeMaker.next(); //addCoffee안에next가 있어서 반복호출 됩니다.
<비동기 작업의 동기적 표현(4) - Promise + Async/await>
비동기 작업을 수행코자 하는 함수 앞에 async 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 됩니다.
Promise ~ then과 동일한 효과를 얻을 수 있습니다.
// 이번에 쓸 방법 : Async(비동기) / await(기다리다)
// Promise ~ then과 동일한 효과를 냅니다.
//coffeeMaker함수에서 호츨할 함수 addCoffee 선언
// Promise를 반환
var addCoffee = function (name) {
return new Promise(function (resolve) {
setTimeout(function(){
resolve(name);
}, 500);
});
};
// var coffeeMaker = async function () {
var coffeeMaker = async () => { // 'async'를 화살표 함수로 쓰는 법
var coffeeList = '';
var _addCoffee = async function (name) {
coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
};
// Promise를 반환하는 전제 하에 await를 만나면 끝날 때 까지 기다려 줍니다.
// ' _addCoffee('에스프레소');' 로직이 끝나는데100초가 걸렸다면
// 그 뒤 consol.log()는 100초 뒤에 시작됩니다.
// 그 아래 await를 만나면 기다리고 끝나면 다음 실행하고를 반복
await _addCoffee('에스프레소');
console.log(coffeeList);
await _addCoffee('아메리카노');
console.log(coffeeList);
await _addCoffee('카페모카');
console.log(coffeeList);
await _addCoffee('카페라떼');
console.log(coffeeList);
};
coffeeMaker();
숙제
Q. 주어진 코드를 async/await 로 리팩토링 하기
class HttpError extends Error {
constructor(response) {
super(`${response.status} for ${response.url}`);
this.name = 'HttpError';
this.response = response;
}
}
function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new HttpError(response);
}
})
}
function koreanMovie() {
return loadJson(`https://klassic-quote-api.mooo.com/v1/random-quote`)
.then(res => {
alert(`${res.author}: ${res.quote}`);
return res;
})
.catch(err => {
if (err instanceof HttpError && err.response.status == 404) {
alert("무언가 에러가 발생했군요!");
return koreanMovie();
} else {
throw err;
}
});
}
koreanMovie();
공부할 때 참고하기 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/rest_parameters
'내일배움 강의 > 완강-JS 문법 종합반' 카테고리의 다른 글
5주차 : DOM과 클래스, 클로저 (7) | 2024.11.11 |
---|---|
3주차 : 데이터 타입(심화), 실행 컨텍스트, this (5) | 2024.11.07 |
2주차_es6, 일급객체로서의 함수, Map과 Set (3) | 2024.11.05 |
JavaScript 문법 종합반 1주차 (1) | 2024.11.05 |