비동기
- 요청한 내용을 언젠간 응답해줄 것을 이라는 약속을 믿고 처리
- 언젠간 응답이 올테니 다른 로직을 수행하고 있자
setTimeout, setInterval
- setTimeout은 일정 시간 간격 이후에 함수가 한번 실행된다
- setInterval은 일정 시간 간격으로 함수가 주기적으로 실행된다
- clearInterval
[정기적인 실행시키기]
- setInterval
- 재귀적인 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 symbols 는 Symbol.match
Symbol.replace
Symbol.search
Symbol.iterator
Symbol.split
등등 이 있다.
'hyunwoo'.search(/woo/); // 4
// 실행시 'hyunwoo'가 String 객체로 변환되고 내부의 search 메서드가
// Symbol.search메서드에 접근
참고글
자바스크립트는 어떻게 작동하는가: 이벤트 루프와 비동기 프로그래밍의 부상, async/await을 이용한 코딩 팁 다섯 가지
자바스크립트 개발자라면 알아야 할 33가지 개념 #10 스케쥴링: setTimeout 과 setInterval
Top comments (0)