DEV Community

Steve Sung
Steve Sung

Posted on

[JS] 비동기프로그래밍 - Callback, Promise, Symbol

비동기

  • 요청한 내용을 언젠간 응답해줄 것을 이라는 약속을 믿고 처리
  • 언젠간 응답이 올테니 다른 로직을 수행하고 있자

setTimeout, setInterval

  • setTimeout은 일정 시간 간격 이후에 함수가 한번 실행된다
  • setInterval은 일정 시간 간격으로 함수가 주기적으로 실행된다
    • clearInterval

[정기적인 실행시키기]

  1. setInterval
  2. 재귀적인 setTimeout
 let timerId = setTimeout(function tick() { alert('tick'); timerId = setTimeout(tick, 2000); // 현재 실행 끝난 후 2초간 딜레이 후 실행 // 딜레이 시간을 쓰지 않으면 바로 동기적으로 실행함 }, 2000); 
  • setInterval 보다 유연하게 이용가능 ex) 서버에 요청이 너무 많아 딜레이를 늘려야 할 때
 let delay = 5000; let timerId = setTimeout(function request() { // 요청 전송 if(서버 과부하 때문에 요청이 실패한다면) { // 다음 실행까지 인터벌을 좀 늘리자 delay *= 2; } timerId = setTimeout(request, delay); }, delay); 
  • setInterval이 보장 못하는 실행간 딜레이를 보장

    • setInterval은 실제로 기재한 시간보다 짧은 딜레이로서 동작
      → 딜레이 시간안에 함수의 동작시간이 포함되기 때문

      → 함수 실행 종료 후에 딜레이가 걸리는게 아닌 단순 시간 을재서 호출을 하게 된다

      → 극단적으로 함수 실행 시간이 길어질 경우 바로 실행될 수도 있다

    • 재귀를 이용한 setTimeout은 함수실행 종료 후 딜레이를 적용하여 호출한다

[제로딜레이 setTimout으로 CPU 소비가 많은 작업을 splitting 하기]

 let i = 0; let start = Date.now(); function count() { // 1부터 1000000000 세는 무거운 작업  // cpu 잠깐 멈출 듯함 for (let j = 0; j < 1e9; j++) { i++; } alert("Done in " + (Date.now() - start) + 'ms'); } count(); // setTimeout으로 스케줄링 let i = 0; let start = Date.now(); function count() { // 약간의 무거운 작업을 해봅시다. 1000000 단위로만 센다 do { i++; } while (i % 1e6 != 0); if (i == 1e9) { alert("Done in " + (Date.now() - start) + 'ms'); } else { setTimeout(count); // 호출을 스케쥴링합니다. // 1000000000에 도달하지 않았다면 다시 동기적으로 실행 } } count(); // 이 경우 작업을 setTimeout으로 나누든 안나누든 큰 차이는 없다 // 차이를 만들기 위한 개선 코드 let i = 0; let start = Date.now(); function count() { // 스케줄링을 함수의 도입부로 옮김 if ( i < 1e9 - 1e6) { setTimeout(count); // 함수 종료 후 실행됨 } do { i++; } while (i % 1e6 != 0); if ( i == 1e9) { alert("Done in " + (Date.now() - start) + 'ms'); } } count(); // 작업전 스케줄링 후 작업 실행시 시간이 적게든다 // 브라우저에 중첩된 타이머 딜레이를 최소화 가능하기 때문 // 브라우저에서는, 중첩된 타이머를 얼마나 자주 동작할 수 있는지에 대한 제한이 있다. // HTML5 표준은 "5개의 중첩된 타이머 이후에는 간격이 적어도 강제로 4ms 만큼 있을 것입니다."  // 라고 말한다. // 보통 실행시 1 1 1 1 9 15 24 ... 중첩되는 타이머가 늘어갈수록 점점 딜레이가 늘어간다 // 스케줄링은 어느 부분에 하느냐에 따라서도 딜레이가 달라지게 된다 

[setTimeout 이용해서 브라우저 렌더 강제시키기]

 <div id="progress"></div>  <script> let i = 0; function count() { for (let j = 0; j < 1e6; j++) { i++; // 현재의 i 값을 progress div에 넣습니다. // innerHTML에 대해 더 알아봅시다. progress.innerHTML = i; // 다 i 작업 끝난후에 html에 반영된다  } } count(); </script>  // setTimeout 적용 <div id="progress"></div>  <script> let i = 0; function count() { // do a piece of the heavy job (*) do { i++; progress.innerHTML = i; } while (i % 1e3 != 0); if (i < 1e9) { setTimeout(count); // 순차적으로 html에 계속 반영 } } count(); </script> 

Callback

  • 프로그래밍에서 콜백(callback)은 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다
  • 콜백함수
    • called at the back on the other function
    • 다른함수의 인자로서 이용되는 함수
    • 어떤 이벤트에 의해 호출되는 함수
  • 콜백지옥 : 비동기 처리 로직을 이용시에 콜백 함수를 연속으로 사용할 때 일단 코드가독성도 떨어지고 안전장치도 없기때문에 정확하게 호출을 확신할수가 없게 되어 버그관리에 취약하게 된다.

Promise

[개요]

  • 비동기 방식을 다루는 최선의 방법을 논하고 있었다.
  • 여러 실험이 벌어지기도 했지만 Node의 표준인 오류-우선 콜백 패턴을 주로 사용하게 되었다
  • Dojo toolkit, JQuery 등이 Promise방식을 사용하면서 Promise를 대세로 만들었다
  • fetch, async 등 중요한 api 들도 새로운 Promise 표준을 기반으로 만들어 졌다

[작동방식]

  • 프로미스에서도 콜백이 사용되긴 하지만 예측가능한 패턴에서만 이용한다
  • 기본개념은 프로미스 기반 비동기적 함수를 호출하면 그함수는 Promise 인스턴스를 반환하게되고 성공 or 실패 두가지만 확인한다.
  • 3가지 state 존재 (pending(대기), fulfilled(이행), Rejected(실패))

    비동기 처리를 다른곳에서 하고싶으면 프로미스 객체를 넘기기만 하면된다.(마치 호출기를 다른사람에게 주듯)

 // 콜백만 적용 function getData(callbackFunc) { $.get('url 주소/products/1', function (response) { callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌  }); } getData(function (tableData) { console.log(tableData); // $.get()의 response 값이 tableData에 전달됨  }); // 프로미스 적용 function getData(callback) { // new Promise() 추가 return new Promise(function (resolve, reject) { // 성공 아니면 실패 $.get('url 주소/products/1', function (response) { // 데이터를 받으면 resolve() 호출  resolve(response); }); }); } // getData()의 실행이 끝나면 호출되는 then() getData().then(function (tableData) { // resolve()의 결과 값이 여기로 전달됨 console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨 }); 
  • resolve() 실행시 프로미스의 state가 fullfilled가 되고 then()을 통해 처리 결과 값을 받을 수 있다.

  • reject() 실행시 프로미스의 state가 rejected가 되고 catch()를 통해 에러 값을 받을수 있다

  • fetch 실행시 성공 실패 여부에 따라 Promise 실행하고 그에따라 then 이나 catch로 실행된다


Symbol

  • 내부 key 값 걱정없이 추가 프로퍼티를 만들수 있는 고유의 값 만들 때 이용

  • 객체 프로퍼티 키 일 수도 있다

  • Symbol로 추가하면 이름 충돌 걱정 하지않고 새 속성 계속 추가가능

 var includes = Symbol('즐거운 자바스크립트'); // Symbol은 객체는 아니다 Array.prototype[includes] = function () { return console.log('its Symbol'); } var arr = [1, 2, 3]; arr.includes(1); // true arr['includes'](1); // true // arr 기본 메서드 동작 arr[includes](); // its Symbol // Symbol('즐거운 자바스크립트'); 가 Key로 들어감 // 충돌하지 않고 Symbol객체로 만든 key 동작 

갑자기 prototype에 같은 이름을 가지는 함수가 새 ES 버전에 생기는 문제 피할 수 있다 → name 충돌방지

well-known Symbol 통해 core method 접근 가능
- 자바스크립트는 자동으로 몇몇 Symbol 변수를 생성하고 global Symbol 객체에 할당한다
- “well-known” global symbolsSymbol.match Symbol.replace Symbol.search Symbol.iterator Symbol.split 등등 이 있다.

 'hyunwoo'.search(/woo/); // 4 // 실행시 'hyunwoo'가 String 객체로 변환되고 내부의 search 메서드가 // Symbol.search메서드에 접근 

참고글

자바스크립트는 어떻게 작동하는가: 이벤트 루프와 비동기 프로그래밍의 부상, async/await을 이용한 코딩 팁 다섯 가지

자바스크립트 개발자라면 알아야 할 33가지 개념 #10 스케쥴링: setTimeout 과 setInterval

Callback이 뭐죠?

콜백

What is a Promise?

자바스크립트 프라미스: 소개 | Web Fundamentals | Google Developers

[Javascript] Symbol 에 대해서

Top comments (0)