혹시나 잘못된 개념 전달이 있다면 댓글 부탁드립니다. 저의 성장의 도움이 됩니다
Asynchronous call
효율성을 위해 작업 요청과 작업 실행이 분리되어 진행하는 상태
작업 요청이 블로킹되지 않는 상태
-> 하나의 작업이 끝나지 않아도 다음 작업이 진행(동시다발적으로 처리)
-> 완료에 도달하는 시간이 더 적음
cf. blocking
: 하나의 작업이 완전히 끝날 때까지 다른 작업을 수행하지 못하는 현상
-> 요청시 즉시 응답(실행) = 동기
동기 Synchronous
: 블록킹으로 인해, 한 작업이 완료될 때 다른 작업이 시작하는 상태
-> 완료시적 = 시작 시점
cf. 비동기가 항상 좋은 방법은 아님 -> 상황에 따라 동기, 비동기 적용
콜백 함수
다른 함수의 전달인자로 넘겨지는 함수
-> 전달받은 함수는 콜백 함수를 필요에 따라 바로 실행하거나 나중에 실행할 수 있음
-> 이벤트에 의해 나중에 실행하면 비동기(바로 실행하면 동기)
function handleClick(){
console.log('버튼이 눌렸습니다!');});
document.querySeletor('#btn').onclick = handleClick;
document.querySeletor('#btn').onclick = function(){ handleClick(); }
document.querySeletor('#btn').onclick = handleClick.bind();
/*
document.querySeletor('#btn').onclick = handleClick();
이 코드는 실행값을 전달한 것으로 잘못된 방법 -> () 없이 사용해야함
이 경우 undefined를 할당한 것
*/
동기작업 코드(블록킹) 예시
: 주문 후 바로 메뉴를 만들어서 전달 후 다음 주문 받음
-> 시간이 오래걸림
// 현재와 시작 시간 비교하여 ms 범위 일때 무한 루프 도는 블로킹 함수
function waitSync(ms){
let start = Date.now();
let now = start;
while(now-start < ms){
now = Date.now();
}
}
function getMenu(person, coffee){
console.log(`${person} 고객님 주문하신 ${coffee}나왔습니다.`);
}
function orderCoffeeSync(coffee){
console.log(`${coffee} 주문이 접수되었습니다.`);
waitSync(5000);
return coffee;
}
const orderLists = [
{name: "김oo", orderMenu: '아이스라떼'},
{name: "이oo", orderMenu: '자바프라프치노'},
{name: "최oo", orderMenu: '아이스티'},
];
orderLists.forEach(function(orderLists){
const menu = orderCoffeeSync(orderLists.orderMenu);
getMenu(orderLists.name, menu);
})
비동기작업 코드(논블록킹) 예시
: 주문을 전부 받고, 메뉴가 완성되면 전달
-> 콜백 패턴 사용
function waitAsync(callback, ms){
setTimeout(callback, ms); // 특정 시간 이후 함수 실행(브라우저 내장기능)
};
function getMenu(person, coffee){
console.log(`${person} 고객님 주문하신 ${coffee}나왔습니다.`);
}
function orderCoffeeAsync(coffee, callback){
console.log(`${coffee} 주문이 접수되었습니다.`);
waitAsync( function(){ callback(coffee); }, 5000 );
return coffee;
}
const orderLists = [
{name: "김oo", orderMenu: '아이스라떼'},
{name: "이oo", orderMenu: '자바프라프치노'},
{name: "최oo", orderMenu: '아이스티'},
];
orderLists.forEach(function(orderLists){
const callback = function(orderMenu){
getMenu(orderLists.name, orderMenu);
}
const menu = orderCoffeeAsync(orderLists.orderMenu, callback);
})
비동기 예시
비동기에서 순서를 제어하는 수단
: 동기 작업은 요청 순서대로 진행되지만, 비동기 작업에서는 먼저 끝나는 작업에 따라 응답 순서가 달라지므로 예상과는 진행순서가 달라질 수 있음
-> 콜백을 사용하여 진행 순서 핸들링
const doingWork = function(work, callback){
const delay = Math.floor(Math.random()*100)+1
const func = () => {
console.log(`${work} 예상 시간은 ${delay}분 걸린다`);
callback();
};
setTimeout(func,delay);
};
const housework = () =>{
doingWork("빨래하기",()=>{
doingWork("설거지하기",() =>{
doingWork("빨래 널기",() =>{})
})
})
}
housework();
// 각 작업의 소요 시간은 다르지만 원하는 순서대로 진행하여 총 작업시간을 줄일 수 있음
// cf. 만약 doingWork 정의할때 콜백을 전달하지 않았다면 efficientHousework에서 같은 순서로 실행하더라도 빠른 순서대로 출력됨
콜백헬
: 콜백으로 순서를 핸들링할 수 있지만 수정할 때 여러가지 오류가 발생할 위험 높음
- 가독성 저하
- 유지보수에 비효율적
ex. 빨래널기, 이후 오늘 할일을 추가했을 때, 마트가 일찍 문닫아서 화분에 물주기 전에 마트갔다오고 순서대로 진행하기로 함 -> 수정하다가 괄호 빠뜨려서 오류 발생
콜백함수를 사용하여 에러 처리하는 방식
-> 에러가 발생하는 상황과 에러가 없을 경우를 나눠서 콜백을 리턴
/* 예시를 위해 node.js 사용 */
const fs = require("fs");
const getDataFromFile = function (filePath, callback) {
// readFile의 3번째 전달인자로 콜백을 전달해야함
// 에러가 발생한 경우에 콜백의 전달인자에 에러를 넣고 데이터를 null 처리
fs.readFile(filePath,'utf8', (err,data) => {
if(err){
return callback(err,null)
} else{
return callback(null,data)
}
}
);
};
getDataFromFile('README.md', (err, data) => console.log(data));
콜백헬이 생기는 것을 방지하기 위해 콜백을 핸들링하는 방법
: 프로미스를 반환하는 함수에 프로미스 메서드를 나열하여 순서 지정
-> .then()
등 추가하여 다음 작업 순서 지정
-> 콜백 방식 보다 가독성 좋음
new Promise(executor);
// 인스턴스의 전달인자로 콜백함수 사용
// 콜백함수 전달인자 2개 : resolve, reject
// (resolve,reject) => { // 콜백함수방식의 내용 + 콜백호출을 resolve 호출로 변경 등 }
실행1()
.then(()=>{return 실행2()})
.then(()=>{return 실행3()})
.catch(/* callback */)
.finally(/* 실패해도 수행 */)
// 실행1은 프로미스를 리턴하는 함수일 때
메서드의 실행 결과로 Promise를 반환
-> Promise chaining 가능
then(callback)
: executor 실행 성공시 실행할 다음 작업 지정catch(callback)
: 오류 발생시 실행할 작업 지정then
이후에 한번 사용 가능finally(callback)
: 실행의 완료 / 실패 여부에 상관없이 항상 수행되는 부분.catch()
이후 가장 마지막에 사용Promise()
내부에 resolve()
나 reject()
가 사용되지 않은 상태도 포함const rejectTest = (message) => {
return new Promise((resolve, reject) =>{
setTimeout(()=>{
reject(new Error(message));
}, 0)
})
}
rejectTest('오류발생');
// rejected 상태
rejectTest('오류발생').catch(err => console.log(err));
// fulfilled 상태
프로미스 실행함수의 parameter는 resolve
, reject
2개
성공/ 실패 여부에 따라 둘 중 하나만 실행됨
resolve()
: 성공적으로 실행될 경우 -> 리턴값은 .then()
의 전달인자로 전달 reject()
: 실행이 거부되거나 에러 발생할 때 핸들링 -> 리턴값은 .catch()
의 전달인자로 전달전달인자
Executor 매개변수가 함수로 사용될 때, 전달인자로 입력 받은 것이 연결된 프로미스 메서드의 전달인자로 전달됨
: resolve("next")
처럼 "next"를 전달하면 연결된 메서드 .then()
전달인자로 사용됨
const doingWork = function(work){
const delay = Math.floor(Math.random()*100)+1
return new Promise((resolve, reject) => {setTimeout(() => {
console.log(`${work} 예상 시간은 ${delay}분 걸린다`);
resolve();
},delay)})
};
const promiseHousework = () =>{
doingWork("빨래하기")
.then(()=> {return doingWork("설거지하기")})
.then(()=> {return doingWork("빨래 널기")})
}
promiseHousework();
// doingWork 내부에서 오류가 발생하면 try,catch 방식 + reject() 추가하여 오류 핸들링
const promise = new Promise(/*생략*/)
promise.then().then().then().then()
Promise를 반환할 경우 계속 메서드를 이어나갈 수 있는 것
-> 프로미스 메서드는 Promise를 반환하므로 프로미스 메서드를 계속 이어서 사용할 수 있음
undefined
가 다음 메서드로 전달
프로미스 헬
: 프로미스 방식으로 선언하더라도then()
의 콜백함수에 return을 적용하지 않을때 발생
->then()
의 콜백에서return
의 중요성
프로미스 방식의 Syntactic sugar
비동기 방식으로 작동하지만 동기적으로 보이게 표현
async
함수 : 내부에서 순서를 정하는 함수 앞에 async
를 추가await
키워드 cf. Syntactic sugar(문법적 설탕)
: 인간이 더 쉽게 이해하고 편하게 사용할 수 있도록, 간결하게 표현하거나 실제 동작과는 다른 방식으로 표현하는 구문을 의미
-> 코드의 가독성을 높이기 위한 방식
Promise를 반환하는 함수 앞에 사용하는 키워드
await
를 적용한 함수는 리턴값을 반환const doingWork = function(work){
const delay = Math.floor(Math.random()*100)+1
return new Promise((resolve, reject) => {setTimeout(() => {
console.log(`${work} 예상 시간은 ${delay}분 걸린다`);
resolve();
},delay)})
};
const promiseHousework = async () =>{
await doingWork("빨래하기")
await doingWork("설거지하기");
await doingWork("빨래 널기");
}
promiseHousework();
// Promise 방식 기반의 표현
// Promise 메서드 대신 리턴값을 활용하여 동기 방식처럼 표현
// resolve()의 전달인자나 함수의 return 값을 반환
// -> 프로미스를 리턴 X
비동기 방식 가독성
async, await 방식 > promise 방식 >>> callback 방식
브라우저에서 제공하는 web API
지정한 시간 뒤에 콜백함수를 실행
clearTimeout()
으로 취소 가능setTimeout(callback, delay, arg1,...,argN);
// delay : millisecond 단위로 callback함수를 해당값만큼 지연 후 실행(타이머)
// -> 1000 입력시 1초 뒤 실행
// arg1, ..., argN : callback의 arguments로 필요한 경우만 입력
const leaveMessage = () => { console.log("3초 뒤 실행"); };
setTimeout(leaveMessage, 3000);
// ID 반환 후 3초 뒤 메세지 남음
setTimeout 타이머 취소
clearTimeout(timeoutID);
// timeoutID : 취소할 대상의 ID
// -> setTimeout실행값을 변수에 담아 전달인자로 넘김(setTimeout의 리턴값)
const timerID = setTimeout(leaveMessage, 3000);
clearTimeout(timerID);
지정한 시간 간격으로 콜백함수를 반복적으로 수행
clearInterval()
로 취소 가능setInterval(callback, delay, arg1,...,argN);
// delay : millisecond 단위로 callback함수를 해당값만큼 지연 후 실행 반복
// -> 1000 입력시 1초 뒤 실행
// arg1, ..., argN : callback의 arguments로 필요한 경우만 입력
const intervalID = setInterval(leaveMessage, 3000);
setInterval 타이머 취소
clearInterval(intervalID);
// intervalID : 취소할 대상의 ID
// -> setInterval실행값을 변수에 담아 전달인자로 넘김(setInterval의 리턴값)
const intervalID = setInterval(leaveMessage);
clearInterval(intervalID);
얕은 복사 후 새 배열 반환
-> immutable
-> const result = [];
-> 동작 실행 -> return result;
느낀점
라이브러리 만들어보는 과제 너무 어려웠다. reduce()같은 고차 함수는 어느정도 이해했다고 생각했는데 아니었다. 물론 그 메서드들의 내용을 구현하는거라 그 함수 자체를 이해못한건 아니지만, 확실하게 콜백함수에서 함수의 변수를 어떻게 적용시켜야하는지는 아직 미숙한 것 같다. 함수 호출이 아닌 정의할 때는 자유롭게 사용할 수 있지만 이게 은근 헷갈린다. 특히 _.each 메서드 구현하는게 이해가 안간다. iteratee(ele, idx, arr) 이 부분.. 그냥 문제에서 하라는 규칙인건가.. 혼란스럽다. 어드벤스드 문제는 커녕 베어미니멈 문제도 완전히 아는 것 같지는 않다. 어드벤스드를 도전하는건 필기 정리, 못한 부분 다 하고 해야겠다. 이러다 할 수 있나...?? 오늘도 금요일이라 css 연습 해보려고했는데... 또 과제하느라 12시다. 이럴땐 쉬운 문제 풀이로 힘을 받아야지!!
개선점 및 리마인드
초조해하지 말자!
오늘 특히 초조해진 것 같다. 불안해하지 말자!!
**