3.1 callback
3.2 Promise
3.3 async/await
console.log('커피 주문을 받을수 있습니다.')
console.log('커피를 주문 받았습니다.')
console.log('커피가 나왔습니다.')
console.log('커피 주문을 받을수 있습니다.')
...
위 상황은 비동기적 상황이다. 코드를 위에서 부터 아래까지 쭉 일어가는 느낌이다.
console.log('커피 주문을 받을수 있습니다.)
console.log('커피를 주문 받았습니다.')
setTimeoug(()=> {console.log('커피가 나왔습니다.')},3000)
console.log('커피 주문을 받을수 있습니다.)
위 상황은 동기적 상황을 연출하였다. 위에서 부터 읽어내려오던 코드는 중간 setTimeout를 건너뛰고 밑의 함수부터 출력하다가 setTimeout함수는 함수가 완료되면 출력한다.
우리가 사용하는 JavaScript는 single threaded언어이다. 따라서 한번에 하나의 작업밖에 수행하지 못한다.
따라서
1) 사용자 이벤트처리
2) 네트워크 응답처리
3) 파일을 일고 쓰는등의 파일 시스템 작업
4) 의도적으로 시간 지연을 사용하는 기능(알람 등)
를 구현하기 위해서는 JavaScript에서는 비동기화를 사용하여야 한다.
자세한 예시1)을 통해 JavaScript의 작동원리를 살펴보고 비동기화를 사용하는 이유를 실생활 예시로 살펴보겠다.
예시1)
위 그림에서 첫번째로 stack에 100초가 걸리는 코드를 넣었을경우 사이트는 그상태에서 아무런 동작을 하지 못하게 된다.
예시2)
그래프를 확인하더라도 많은 코드를 한번에 수행하는 비동기화는 매우 효율적이라는것을 알 수 있다.
예시3)
흔히 우리가 접하는 Youtube의 화면 구성을 예시로 들었을때 화면출력부분이 로딩되고 있을때에도 비동기적인 활동으로 인해 다른 활동(재생목록 송출,출력화면 설명구성,검색바 버튼클릭 활성준비)을 할수 있다.
!)대표적인 콜백함수인 event litener
function callback() { alert("Hello");} document.getElementById("meet").onclick = callback() //이벤트 발생시 callback함수 실행
!)함수의 실행 순서를 제어할 수 있다.
const print = (string,callback) =>{ setTimeout( ()=>{ console.log(string) callback()}, 2000)} const printall = () => { print('a',() =>{ // 실행이 되면 callback함수를 실행 print('b',()=>{ // 실행이 되면 callback함수를 실행 print('c',()=>{}) //실행이 되면 callback함수를 실행 }) }) } printall() //2초간격으로 'a','b','c' 출력
!!)callback error handling Design
function checkfile(filePath, callback) { //callback이라는 에러를 처리하는 함수 fs.readFile(filePath, "utf8", (error, data) => {//fs호출을 통해 에러유무 확인 if (error) callback(error, null);//에러일경우 callback에 error 변수 대입 else callback(null, data);//아닐시 정상적인 데이터를 콜백에 넣는다. }) }
무분별한 콜백을통해 콜백지옥을 경험하면 어떤함수를 호출하는지 정확한 가독성이 매우 떨어진다.
이러한 가독성 문제를 해결하고자 나온것이 바로 Promise함수이다.
!) Promise3가지 형태
- 대기(new Promise호출시 pending상태가 됩니다.)
new Promise(function(resolve, reject){ //정상적인 데이터 처리시 resolve, 데이터 처리 실패시 reject함수 인자를 호출한다. });
- 이행(resolve를 실행하면 이행상태가 됩니다)
function getData() { return new Promise(function(resolve, reject) { const data = 'Promise' resolve(data); //resolve함수에 data값을 넣는다. }); } // then을 통해 Pushdata함수의 데이터를 받아온다 // resolve()의 결과 값 data를 resolvedData로 받음 getData() .then((readData)=> { console.log(readData); //'Promise' });
- 실패(reject를 호출하면 실패상태가 됩니다)
function getData() { return new Promise(function(resolve, reject) { const data = 'Promise' resolve(data) const none = 'none Data' reject(new Error(('data'))); //resolve함수에 data값을 넣는다. }); } // then을 통해 Pushdata함수의 데이터를 받아오며 catch로 // resolve()의 결과 값 data를 resolvedData로 받음 getData() .then((readData)=> { console.log(readData); //'Promise' }) .catch((err)=> { console.log(err); // none Data });
- 여러개의 Promise를 연결(Promise Chaining)
new Promise(function(resolve, reject){ setTimeout(function() { resolve(1); }, 2000); }) .then(function(result) { //Promise로 부터 값을 받아온다. console.log(result); // 1 return result + 1; //return으로 다음 then에 값을 전달한다. }) .then(function(result) { //이전 then으로 부터 값을 받아온다. console.log(result); // 2 return result + 1; //return으로 다음 Promise에 값을 전달한다. }) .then(function(result) { console.log(result); // 3 });
중요!! then은 Promise를 반환한다.
Promise 객체를 반환한다는 것은 then, catch메소드를 사용할 수 있다는 것을 뜻하며, 이를 통해 연속적으로 then메소드를 사용하여 Promise chaining이 가능하다는 것을 의미한다.
Tip) Promise.all
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// fetch를 사용해 url을 프라미스로 매핑합니다.
let requests = urls.map(url => fetch(url));
// Promise.all은 모든 작업이 이행될 때까지 기다립니다.
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
[출처: https://lienkooky.tistory.com/98 [Lien _ Blog]]
-기존의 콜백 함수와 프로미스의 단점을 보완하고 가독성 좋은 코드를 작성할 수 있게 해준다.
function get(){
return new Promise((resolve,reject) => {
resolve('done')})}
async function get() {
return done}
둘다 같은 Promise를 반환한다.
- 기본틀
async function 함수명() { await 비동기_처리_메서드_명(); }
- 예제1)
function getData() { return new Promise(function(resolve, reject) { const data = 'Promise' resolve(data); //resolve함수에 data값을 넣는다. }); } // then을 통해 Pushdata함수의 데이터를 받아온다 // resolve()의 결과 값 data를 resolvedData로 받음 async function readData(){ const resultData = await getData() console.log(resultData) //'Promise' //await를 사용하지 않았다면 콜백이나 .then을 사용했어야 합니다. 하지만 async await 문법덕분에 코드가 간결해지며 비동기에 대한 사고를 적게하면 됩니다.
- 예외처리)
function getData() { return new Promise(function(resolve, reject) { const data = 'Promise' resolve(data); //resolve함수에 data값을 넣는다. const none = 'none Data' reject(new Error(('data'))); //resolve함수에 data값을 넣는다. }); } // then을 통해 Pushdata함수의 데이터를 받아온다 // resolve()의 결과 값 data를 resolvedData로 받음 async function readData(){ const resultData = await getData() console.log(resultData) //'Promise' try{ await getData() }catch(err){ console.log(err) } } OR async function readData(){ const resultData = await getData() console.log(resultData) //'Promise' try{ await getData().catch((err) => console.log(err)) }catch(err){ console.log(err) } } OR async function getData(){ throw 'none Data'} getData().catch(){(err)=>{ console.log(err) //'none Data' } //await를 사용하지 않았다면 콜백이나 .then을 사용했어야 합니다. 하지만 async await 문법덕분에 코드가 간결해지며 비동기에 대한 사고를 적게하면 됩니다.