동기의 대략적인 특징
• 동시에 여러 작업을 수행할 수 없다.
• 흐름을 예측하기 쉽다. 먼저 수행되고 나중에 수행되는 것들이 명확하다.
비동기의 대략적인 특징
• 동시에 여러 작업을 수행할 수 있다.
• 흐름을 예측하기 어렵다. 즉 무엇이 먼저 완료될 지 보장할 수 없다.
정의 : 함수에 파라미터로 들어가는 함수
용도 : 순차적으로 실행하고 싶을 때 사용.
특징 : 다른데서 만든 함수도 콜백함수로 넣을 수 있다.
함수명도 작명 가능.
콜백함수가 필요한 함수들에만 콜백함수 사용가능.
Document.querySelector(‘ . button’).addEventListener(‘click, 함수명)
seTimeout(function 함수명() {
}, 1000)
원리 :
Function first (파라미터){
Console.log(1)
파라미터 ()
}
Function second(){
Console.log(2)
}
first(second)
쓰는 이유: first 라는 함수를 만들었는데 유용해서 팀원들이 자주 쓸때
First() 후에 console.log(2) 를 바로 사용하고 싶음.
그럴때 콜백 함수를 사용함으로써 편하게 갖다 사용할 수 있다
보통 남이 쓴 코드 가져다 쓸 때 가끔 등장.
단점 : DB데이터 뽑을때
A뽑고 b뽑고 c뽑을때
db.collection(‘post’). findOne(A, function(){
db.collection(‘post’). findOne(B, function(){
db.collection(‘post’). findOne(C, function(){
})
})
})
이런식으로 코드가 더러워짐.
이럴 때 Promise 사용.
더 복잡해지면 async await 사용.
비동기 작업 단위.
예시 :
const promise1 = new Promise((resolve, reject) => {
// 비동기 작업
});
new Promise(...) 로 Promise 객체를 새롭게 만듬. 생성자는 함수와 동일하게 동작하므로, 괄호() 를 써서 함수를 호출하는 것과 같은 모습이라 할 수 있음.
생성자는 특별한 함수 하나를 인자로 받음. (여기서 인자로 들어가는 함수의 형태는 화살표 함수)
이 화살표 함수 ? -> executor라 불리는 함수
new Promise 는 그냥 기다리지 않고 바로 호출.
Promise 가 끝나고 난 다음의 동작을 설정해줄 수 있는데, 그것이 바로 then 메소드와 catch 메소드
then 메소드는 해당 Promise 가 성공했을 때의 동작을 지정. 인자로 함수를 받음
catch 메소드는 해당 Promise 가 실패했을 때의 동작을 지정. 인자로 함수를 받음.
위 함수는 연속적으로 호출이 가능.
ex)
const promise1 = new Promise((resolve, reject) => {
resolve();
});
promise1
.then(() => {
console.log("then!");
})
.catch(() => {
console.log("catch!");
});
실행결과
then!
바로 resolve 가 호출되었기 때문에 성공으로 간주하여 then 에 있는 동작만 실행
ex)
const promise1 = new Promise((resolve, reject) => {
reject();
});
promise1
.then(() => {
console.log("then!");
})
.catch(() => {
console.log("catch!");
});
실행결과
catch!
위 작업들은 기다리는 작업이 하나도 x 기다리는 작업이 없다면 비동기를 쓸 이유가 없음.
요약 : Promise 를 만드는 순간 비동기 작업이 시작되며, 비동기 작업을 성공으로 간주하고 싶을 때 resolve를 호출하고, 실패라 간주하고 싶다면 reject 함수를 호출합니다. 이 비동기 작업이 성공했을 때 후속 조치를 지정하고 싶다면 then으로, 실패 시의 후속 조치는 catch 로 지정.
비동기 작업을 손쉽게 만드는 방법.
async 키워드는 함수를 선언할 때 붙여줄 수 있음. async 키워드가 붙은 함수를 async 함수로, async 가 없는 함수는 일반 함수라고 부르도록 하겠다.
async 함수는 Promise 와 굉장히 밀접한 연관을 가지고 있는데, 기존에 작성하던 executor 로부터 몇 가지 규칙만 적용한다면 new Promise(…) 를 리턴하는 함수를 async 함수로 손쉽게 변환 가능.
How?
함수에 async 키위드를 붙임.
new Promise... 부분을 없애고 executor 본문 내용만 남긴다.
resolve(value); 부분을 return value; 로 변경.
reject(new Error(…)); 부분을 throw new Error(…); 로 수정
결과물
// 기존
// function startAsync(age) {
// return new Promise((resolve, reject) => {
// if (age > 20) resolve(`${age} success`);
// else reject(new Error(`${age} is not over 20`));
// });
// }
async function startAsync(age) {
if (age > 20) return `${age} success`;
else throw new Error(`${age} is not over 20`);
}
const promise1 = startAsync(25);
promise1
.then((value) => {
console.log(value);
})
.catch((error) => {
console.error(error);
});
const promise2 = startAsync(15);
promise2
.then((value) => {
console.log(value);
})
.catch((error) => {
console.error(error);
});
위 함수에서는 문자열을 리턴했는데, promise1과 promise2 는 문자열이 아님! 무조건 async 함수를 실행시킨 뒤 then 과 catch 를 활용하여 흐름을 제어해야 한다.
Promise 가 끝날 때까지 기다리거라.
await 는 Promise 가 fulfilled 가 되든지 rejected 가 되든지 아무튼 간에 끝날 때까지 기다리는 함수.
쓰임새는 이와 같고, await 은 또 쓸 수 있는 제약 조건이 있다. 바로 async 함수 내부에서만 사용할 수 있음.
function setTimeoutPromise(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
async function startAsync(age) {
if (age > 20) return `${age} success`;
else throw new Error(`${age} is not over 20`);
}
async function startAsyncJobs() {
await setTimeoutPromise(1000);
const promise1 = startAsync(25);
try {
const value = await promise1;
console.log(value);
} catch (e) {
console.error(e);
}
const promise2 = startAsync(15);
try {
const value = await promise2;
console.log(value);
} catch (e) {
console.error(e);
}
}
startAsyncJobs();
실행결과
25 success
Error: 15 is not over 20
at startAsync (/home/taehoon/Desktop/playground-nodejs/index.js:17:14)
at startAsyncJobs (/home/taehoon/Desktop/playground-nodejs/index.js:29:20)
startAsyncJobs 함수를 새로 만들었다. 이 함수 내에서 await 을 사용하기 위해 async 함수로 정의내린 후, 코드의 마지막 부분에서 호출함으로써 비동기 작업을 시작. 기존의 then 과 catch 하던 작업들은 모두 이 함수 내에 있음.
요약.
기다리기만 하면 되는 작업을 비동기로 처리할 수 있음. 동시에 여러 작업이 진행되어서 비교적 효율적이지만, 흐름 제어는 동기 코드보다 어렵다.
Promise 를 생성할 때에는 resolve, reject 함수를 적절히 호출하는 작업을 넣어주고, 이후 생성된 Promise 에 대해 then, catch 메서드를 호출하여 후속 조치를 정해줍니다.
new Promise(…)는 async 함수로 적절하게 변환할 수 있음.
async 함수 내에서 Promise 에 대해 await 을 걸어서 작업을 기다릴 수 있음.
스타일은 되도록 일관되게 작성하는 게 좋다. async-await 을 사용하든지, resolve-reject-then-catch 를 사용한다.
여러 Promise 를 동시에 기다리려면 Promise.all 를 사용함.