동기 vs 비동기
기다리고 난 후
다음 코드를 수행
하는 것비동기 javascript
-setTimeout (callback, millisecond) , clearTimeout(timerId)
setTimeout(function () {console.log('1초 후 실행');}, 1000);
clearTimeout(timer);
-setInterval(callback, millisecond) , clearInterval(timerId)
const timer = setInterval(function () {console.log('1초마다 실행');}, 1000);
clearInterval(timer);
그렇다면 비동기 코드에서 주의해야할 점은 무엇일까!
바로 비동기 코드에서는 작성된 순서 ❌ 동작이 완료되는 순서⭕로 진행된다는 것이다.
예제로 보자면,
const printString = (string) => {
setTimeout(function () {
console.log(string);
}, Math.floor(Math.random() * 100) + 1);
};
const printAll = () => {
printString('A');
printString('B');
printString('C');
};
printAll();
위와 같은 예제에서'A''B''C'는 동작이 완료되는 동작 순서로 작동된다.
이러한 비동기 함수를 제어할 수 있는 방법은, 바로 callback 함수이다.
const printString = (string, callback) => {
setTimeout(function () {
console.log(string);
callback();
}, Math.floor(Math.random() * 100) + 1);
};
const printAll = () => {
printString('A', () => {
printString('B', () => {
printString('C', () => {});
});
});
};
그.러.나
위와 같이 제어할 수는 있지만, 콜백함수의 지옥에 빠져버린다....
이걸 callback Hell이라고 한다.
이 때 필요한 것이 바로!
promise는 class이기 때문에, new 키워드를 통해서 객체를 생성한다.
또한, promise는 비동기 처리를 수행할 콜백함수를 인수로 전달받는데,
이 콜백함수는 resolve,reject 를 인자로 전달받는다.
let promise = new Promise((resolve, reject) => {
// 1. 정상적으로 처리되는 경우
// resolve의 인자에 값을 전달
resolve(value);
// 2. 에러가 발생하는 경우
// reject의 인자에 에러메세지를 전달
reject(error);
});
-Promise 객체의 내부 프로퍼티
new Promise가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖습니다. 하지만 직접 접근할 수 없고 .then, .catch, .finally 의 메서드를 사용해야 접근이 가능합니다.
-State
기본 상태는 pending(대기) 입니다. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동했다면 fulfilled (이행)로 변경이 되고, 에러가 발생했다면 rejected (거부)가 됩니다.
-Result
처음은 undefined 입니다. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하여resolve(value)가 호출되면 value로, 에러가 발생하여 reject(error)가 호출되면 error로 변합니다.
executor에 작성했던 코드들이 정상적으로 처리가 되었다면 resolve 함수를 호출하고 .then 메서드로 접근한다.
또한 .then 안에서 리턴한 값이 Promise면 Promise의 내부 프로퍼티 result를 다음 .then 의 콜백 함수의 인자로 받아온다.
Promise가 아니라면 리턴한 값을 .then 의 콜백 함수의 인자로 받아올 수 있다.
let promise = new Promise((resolve, reject) => {
resolve("성공");
});
promise.then(value => {
console.log(value);
// "성공"
})
executor에 작성했던 코드들이 에러가 발생했을 경우에는 reject 함수를 호출하고 .catch 메서드로 접근할 수 있습니다.
let promise = new Promise(function(resolve, reject) {
reject(new Error("에러"))
});
promise.catch(error => {
console.log(error);
// Error: 에러
})
executor에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally 메서드로 접근할 수 있습니다.
let promise = new Promise(function(resolve, reject) {
resolve("성공");
});
promise
.then(value => {
console.log(value);
// "성공"
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log("성공이든 실패든 작동!");
// "성공이든 실패든 작동!"
})```
### Promise chaining
Promise chaining가 필요하는 경우는 비동기 작업을 순차적으로 진행해야 하는 경우입니다. Promise chaining이 가능한 이유는 .then, .catch, .finally 의 메서드들은 Promise를 반환하기 때문입니다. 따라서 .then을 통해 연결할 수 있고, 에러가 발생할 경우 .catch 로 처리하면 됩니다.
```javascript
let promise = new Promise(function(resolve, reject) {
resolve('성공');
...
});
promise
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.catch((error) => {
console.log(error);
return '실패';
})
.finally(() => {
console.log('성공이든 실패든 작동!');
});
그래서 돌고돌아 아까 콜백함수의 지옥에 빠졌던 예제를 promise로 다시 작성해보자면
const printString = (string) => {
return new Promise((resolve,reject)=> {
setTimeout(()=> {
resolve(); console.log(string); // 리턴 값 앞에 resolve가 있는것을 볼 수 있다!
},Math.floor(Math.random() * 100) + 1);
});
};
const printAll=() => {
printString('A').then (()=> {return printString('B') //printString안에 resolve가 있고, 그 뒤에 then이 와서 받아준다!
})
.then(() => {
return printString('C');
});
};
printAll(); //A B C
-> 여러 개의 비동기 작업을 동시에 처리하고 싶을때 사용
const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));
위의 코드를 promise chaining을 사용하면 코드들이 순차적으로 동작하여 총 6초
의 시간이
걸린다. 또한, 코드도 중복되어서 보기에 좋지않다.
//promise chaining을 사용한 모습
const result = [];
promiseOne()
.then(value => {
result.push(value);
return promiseTwo();
})
.then(value => {
result.push(value);
return req3();
})
.then(value => {
result.push(value);
console.log(result);
// ['1초', '2초', '3초']
})
// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
.then((value) => console.log(value))
// ['1초', '2초', '3초']
.catch((err) => console.log(err));
promise.all에 배열형태로 나눠주고, then,catch 메서드를 통해서 부른다.
앗, 그러면 promise를 통해서 비동기를 처리해주면 코드가 간편해지겠군요?🤔
그렇지도 않다....
promise도 callback 함수와 같이 코드가 길어질수록 복잡하고 가독성이 낮아지는
promise hell이 있다....!!!!!😡 밑에서 promise hell코드 겪어보세요....
const printString = (string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(string);
}, Math.floor(Math.random() * 100) + 1);
});
};
const printAll = () => {
printString('A').then((value) => {
console.log(value);
printString('B').then((value) => {
console.log(value);
printString('C').then((value) => {
console.log(value);
printString('D').then((value) => {
console.log(value);
printString('E').then((value) => {
console.log(value);
printString('F').then((value) => {
console.log(value);
printString('G').then((value) => {
console.log(value);
printString('H').then((value) => {
console.log(value);
printString('I').then((value) => {
console.log(value);
printString('J').then((value) => {
console.log(value);
printString('K').then((value) => {
console.log(value);
printString('L').then((value) => {
console.log(value);
printString('M').then((value) => {
console.log(value);
printString('N').then((value) => {
console.log(value);
printString('O').then((value) => {
console.log(value);
printString('P').then((value) => {
console.log(value);
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
};
printAll();
음...........이러면 대체 뭘 써야하나요! 😲😲
그래서 javascript는 ES8에서 async/await 키워드를 제공했다...!😉
이 async/await 키워드는..!
함수 앞에 async 키워드를 사용하고 async 함수 내에서만 await 키워드를 사용하면 된다. 이렇게 작성된 코드는 await 키워드가 작성된 코드가 동작하고 나서야 다음 순서의 코드가 동작하게 되는 것이다..!!
// 함수 선언식
async function funcDeclarations() {
await 작성하고자 하는 코드
...
}
// 함수 표현식
const funcExpression = async function () {
await 작성하고자 하는 코드
...
}
// 화살표 함수
const ArrowFunc = async () => {
await 작성하고자 하는 코드
...
}