구글에 입사하기 위해서 2020년 기준 자바스크립트 질문들에 대해서 소개드립니다.
부제 : Callbacks, Promises, Await/Async
1. Callback
간단한 callback 예시
const body = document.getElementsByTagName('body')[0];
function callback() {
console.log('Hello');
}
body.addEventListener('click', callback);
EventListener 이벤트 함수도 콜백을 사용합니다.
- 이 경우 "콜백"이라는 함수를 다른 함수 "addEventListener"에 전달합니다.
- addEventListener가 실행되면 click 이벤트와 함께 콜백을 등록합니다.
여기서 JavaScript를 실행하는 방법에는 두 가지가 있습니다.
1. 페이지가 로드 될 때 스크립트가 한 번 실행됩니다.
- body가 상수로 선언되고 body 엘리먼트가 할당됩니다.
- callback 함수가 선언되지만, 실행되지 않습니다.
- addEventListener가 실행됩니다.(콜백은 addEventListener로 전달됩니다.)
- body에 클릭 이벤트가 일어나면 addEventListener가 실행해야될 일 중 하나로 브라우저에게 콜백 함수를 실행하라고 합니다.
2. JavaScript가 실행되는 다른 시간은 이벤트가 발생할 때입니다
- 이번에는 콜백이 마지막으로 실행됩니다.
- body를 클릭하면 콜백이 실행되고 콘솔에 "Hello"가 기록됩니다.
- 이것은 두 번 이상, 즉 본문을 클릭 할 때마다 발생할 수 있습니다.
callback의 실제 예제
스크립트와 모듈을 비동기적으로 로드하는 loadScript라는 함수를 만들어 보겠습니다.
function loadScript(src) {
// script태그를 만들고 document의 head에 append 합니다.
let script = document.createElement('script');
script.src = src;
document.head.append(script);
}
// 실행
loadScript('/my/script.js');
브라우저는 자동으로 로드를 시작하고 완료되면 실행됩니다.
문제점
loadScript('/my/script.js');
// 아래의 함수는 loadScript를 기다려주지 않습니다.
newFunction(); // 실행되지 않음!
해결
스크립트가 로드 될 때 실행되어야 하는 loadScript에 두 번째 인수로 콜백 함수를 추가해 보겠습니다.
// loadScript를 정의
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
// 실행
loadScript('/my/script.js', function() {
// script가 다 로드되면 callback이 실행됩니다.
newFunction(); // 작동!
});
두 번째 인수는 작업이 완료 될 때 실행되는 함수(일반적으로 익명)입니다.
callback 안의 callback
Promise와 Async/Await 예제를 살펴보기 전에 한 가지 예시를 더 보고 넘어가죠.
어떻게 두 개의 스크립트를 순차적으로 로드 할 수 있습니까?
자연스러운 해결책은 두 번째 loadScript 호출을 콜백 안에 넣는 것입니다.
loadScript('/my/script.js', function(script) {
loadScript('/my/script2.js', function(script) {
loadScript('/my/script3.js', function(script) {
// 순차적으로 실행됩니다. 모든 scrip가 로드되고 실행되는 구간
});
})
});
2. Callback vs.Es6 Promise
운명의 피라미드라는 말과 콜백 지옥이라는 말이 있습니다.
콜백 지옥을 resolve(해결) 하기 위해서 만들어졌습니다.
// promise 방식으로 loadScript를 변경하였습니다.
function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
// script가 로드가 되면 callback()함수를 실행하던 것에서 resolve(script)를 호출하는 것으로 변경
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(script);
});
}
// 사용법
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
// 변수에 담아서 그 변수에 담긴 return new Promise 함수가 호출이 완료되면 resolve 또는 reject를 then으로 받습니다.
promise.then(
script => alert(`${script.src} is loaded!`), // script를 resolve로 보냈으니까 이렇게 받을 수 있습니다.
error => alert(`Error: ${error.message}`) // reject 일 때는 error를 만들어서 보낸 것을 이렇게 받습니다.
);
promise.then(script => alert('Another handler...'));
3. Sample - 1번 문제 A,B,C를 순서대로 찍으세요.
callback
function printString(string, callback) {
setTimeout(
() => {
console.log(string)
callback()
},
1 * 1000 // 1s
)
}
function printAll() {
printString("A", () => {
printString("B", () => {
printString("C", () => {})
})
})
}
printAll()
* 중요 : 4번째 함수를 에러를 방지하기 위해서 빈 함수를 호출해야합니다.
Promise
function printString(string) {
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
1 * 1000
)
})
}
function printAll() {
printString("A")
.then(() => {
return printString("B")
})
.then(() => {
return printString("C")
})
}
printAll()
async/await
function printString(string) {
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
1 * 1000
)
})
}
async function printAll() {
await printString("A")
await printString("B")
await printString("C")
}
printAll()
new Promise로 return되는 함수를 async로 감싸주고 then으로 순서를 이을 필요 없이 await함수로 호출해줍니다.
4. Sample - 2번 문제 알파벳 배열 ["A","B","C"] 를 사용하여 인쇄
여기에서 조금 더 심화하면 A,B,C string 대신 배열인 ["A","B","C"] 를 제공합니다.
function printAll() {
const arr = ["A", "B", "C"]
let index = -1
printString(arr[++index], () => {
printString(arr[++index], () => {
printString(arr[++index], () => {})
})
})
}
printAll()
5. Sample - 3번 문제 알파벳 배열 [A… Z]을 순서대로 인쇄
콜백 지옥 대신에 여러번 콜백하고 싶은 심정입니다.
최적화를 해보지요!
- arr배열에 알파벳들을 담은 뒤에 printString 호출 배열 [index ++] 후 매번 1 씩 증가하는 index를 정의합니다.
- printString 함수에서 3 개의 매개 변수를 전달합니다 (arr, index, callback의 항목).
- 가장 중요한 것은 cb0fcb 함수를 호출하는 것입니다
function printString(string, indx, cb){
if(indx == 27) {
cb('err')
}
cb(null, string)
}
function callback(err, str) {
if(err) {
console.log('done');
return
}
console.log(str);
}
function printAll(){
let arr = [...Array(26)].map((val, i) => String.fromCharCode(i + 65));
// console.log(arr) // ["A", "B", "C", "D" ... "Z"]
let index = 0;
setTimeout(function cbOfcb() {
let array = arr
if(index < 27){
printString(array[index++], index, callback); // 전달 매개변수 - string , index , callback
cbOfcb(); // if 조건에 맞을 때 까지 재귀 함수를 호출합니다.
}
}, 1000)
}
printAll()
6. Sample - 4번 문제 주어진 횟수만큼 콜백 API 요청하기
// run the request. this function will call itself max. 5 times if the request fails
request(5, callback);
function request(var retries, var callback) {
axios.post("URL").then(
response => {
if(response.data == 1) {
// request가 성공적으로 호출되면 callback함수를 호출합니다.
callback(response);
}
else {
// 실패했으면 retries 횟수를 체크하고 없으면 호출하지 않습니다.
if(retires > 0) {
request(--retires, callback);
}
else {
// 아무런 retries 횟수가 없으면 callback으로 error를 뱉습니다.
callback([], "out of retries");
}
}
})
.catch(error => {
// ajax 서버 오류 발생! -> 다시 리트라이 횟수만큼 리트라이해봅니다.
if (retries > 0) {
request(--retries, callback);
}
else {
// retries가 있는지 체크하고 없으면 그냥 에러입니다.
callback([], error);
}
});
}
감사합니다.
출처 : medium.com/developers-tomorrow/google-javascript-technical-interview-7a20accd6ddf
'면접,이직' 카테고리의 다른 글
프론트엔드 면접 문제은행에 대한 정리(1) (0) | 2020.07.29 |
---|---|
프론트 엔드 기술 면접 질문 리스트 (4) | 2018.04.12 |
댓글