DocumentDB #2 DocumentDB – Lambda 연결하기 (use Nodejs, Mongoose)
Javascript #5 자바스크립트 Promise와 Async/Await에 대해 알아봅니다.
2018-10-26
Explanation
오늘은 자바스크립트의 ES6 표준에 정의된 Promise와 ES8 초안에 들어온 Async/Await에 대하여 적어보려합니다. Generator도 함께 적으면 좋을거 같지만.. 이미 쓴적이 있어서 궁금하신 분은 아래 링크를 참고하세요.
https://lab.falsy.me/Generator-Co-Async-를-이용한-비동기-순차-실행시키기
Promise 약속, 정말 좋은 이름인 거 같아요. Promise는 비동기 통신에서 사용되며 통신의 응답에 대한 약속이라고 생각하면 될 거 같아요.
필요로 하는 부분을 요청하고 요청에 대한 성공 또는 실패 여부를 약속합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 파라미터: 요청할 URL function get(url) { // resolve(성공), reject(실패) 응답에 대한 약속을 합니다. return new Promise((resolve, reject) => { const req = new XMLHttpRequest(); // url로 필요로 하는 값을 ('GET')요청합니다 req.open('GET', url); req.onload = () => { // 성공했다는 응답을 받으면 if(req.status === 200) { // 성공한 내용을 응답합니다. resolve(req.respnse); } else { // 성공하지 못했다는 응답을 받으면 // 실패한 내용을 응답합니다 reject(Error(req.statusText)); } }; // 네트워크 오류 req.onerror = () => { // 네트워크 오류를 응답합니다. reject(Error('network error')); }; req.send(); }); } get('http://test.com/text.json') .then(res => { // 성공했다면 응답받은 내용을 로그로 출력합니다. console.log(res); }).catch(err => { // 실패했다면 실패한 내용을 로그로 출력합니다. console.log(err); }); |
아래에서 예를 적겠지만, Promise는 대기(pending), 이행(fulfilled), 거부(rejected) 이렇게 3가지의 상태값을 가지고 있습니다.
Async 비동기, Await 기다리다.
어쩌면 이 부분을 적으려고 이 글을 쓰기 시작한거 같아요. 전 아래의 예제가 재미있었어요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function wait(time) { return new Promise(function(res, rej) { setTimeout(() => { // 파라미터로 넘어온 시간 만큼 이후에 'ok'라는 값을 응답합니다. res('ok'); }, time); }); } async function series() { const wait1 = await wait(500); const wait2 = await wait(500); return "done!"; } async function parallel() { const wait1 = wait(500); const wait2 = wait(500); const responseWait1 = await wait1; const responseWait2 = await wait2; return "done!"; } |
아직 Async/Await를 지원하지 않는 브라우저가 많아서 Node(v7.6+)에서 확인해보는게 좋을거 같습니다.
function 앞에 async를 붙여서 이 함수가 비동기 함수임을 선언하고 await를 만나면 그 함수의 응답을 기다려요. 결과적으로 위 함수 series를 실행하면 ‘done!’이라는 응답값을 받기까지 약 1초의 시간이 걸리고 parallel는 약 0.5초의 시간이 걸린답니다.
간단하게 이야기하면,
series()는 wait1에서 통신의 시작부터 끝까지를 기다려요(약500ms) 그리고 기다림이 끝나고 나면 다시 wait2에서 통신의 시작부터 끝까지를 기다리죠(약500ms) 그래서 마지막 리턴까지 약 1000ms의 시간이 걸려요.
하지만 parallel()는 응답과 상관없이 wait1과 wait2의 통신을 거의 동시에 요청해요. 그리고 바로 responseWait1은 wait1의 응답을 기다린답니다. 위 예의 경우에는 responseWait1가 wait1의 응답을 받을 쯤에는 이미 wait2에는 응답값을 가지고 있겠죠? 그래서 그 순서는 유지하면서 시간으로는 이득을 볼 수 있어요.
이 글과 이 예제가 비동기 통신을 병렬적인 수행하는 것이 무조건 좋다고 이야기하려는건 아니에요. 이러나 저러나 브라우저에서는 병렬 통신이 4~6개 정도로 제한이 되기도 하고, 한번에 많은 통신은 서버나 브라우저에 순간적으로 많은 부하가 될 수도 있어요.
그리고 위에 parallel()에서 아래와 같이 코드를 테스트 해보면,
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... async function parallel() { const wait1 = wait(500); const wait2 = wait(500); console.log(wait1); const responseWait1 = await wait1; const responseWait2 = await wait2; return "done!"; } parallel(); // console.log(wait1); // Promise { <pending> } |
wait1에서 요청을 하고 바로 wait1에 console.log를 찍어보면 아까 위에서 이야기 했던 Promise의 ‘대기(pending)’ 상태 값을 확인 할 수 있습니다.
https://developers.google.com/web/fundamentals/primers/promises
https://developers.google.com/web/fundamentals/primers/async-functions
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/await
https://medium.com/@constell99/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-async-await-%EA%B0%80-promises%EB%A5%BC-%EC%82%AC%EB%9D%BC%EC%A7%80%EA%B2%8C-%EB%A7%8C%EB%93%A4-%EC%88%98-%EC%9E%88%EB%8A%94-6%EA%B0%80%EC%A7%80-%EC%9D%B4%EC%9C%A0-c5fe0add656c
참고 링크 중, ‘Async/Await이 Promise를 사라지게 만들 수 있다’는 글이 있는데 이는 그냥 관심을 끌기 위한 제목일 뿐, Async/Await와 Promise를 대립적으로 비교할 수 있는 요소들이 아닌거 같아요. 둘은 비동기 통신에서 함께 사용된다고 생각하는게 맞을 거 같아요.