LHJ

I'm a FE developer.

15. iterable 객체 연습 - 브라우저의 동기적 명령처리 허용시간

10 Aug 2020 » codespitz_re

iterable 객체 연습 - 브라우저의 동기적 명령처리 허용시간

허용시간에 대한 개념이 없으면 루프를 과도하게 돌려 블로킹을 엄청 일으키는 소스를 짜게될 것이다.
브라우저는 과연 어느정도까지 여러분들의 소스가 블로킹하는걸 봐주고 있을까?
PC용 브라우저는 보통 30초 정도 봐준다고 알려져있지만,
계속된 브라우저 업데이트로 요즘은 20초도 안봐준다.

모바일에선 어떨까?
크롬 같은 경우는 크로니움 소스가 안드로이드와 완전히 동일하기 때문에 안드로이드에 있는 크롬과 PC에 있는 크롬 소스가 완전히 똑같다.
그렇기 때문에 모바일도 똑같이 20초 정도 적용을 받을 것이다.

그러나 안드로이드 OS는 5초 이상의 블로킹이 일어나면 앱을 중지시키는 경향이 있다.
브라우저에서 20초를 허용해도 소용없는 것이다.

마찬가지로 window 10은 제어퀀을 window OS가 들고 있다.
윈도우 OS도 15초를 넘기면 마음에 안든다고 중지시키는 기능이 있다.
여러분들이 블로킹을 걸 수 있는 범위는 5초 이내라고 보는 것이 정상이다.

문제는 5초동안 블로킹 당하는 시간동안 얼만큼 많이 일할 수 있느냐이다.
그건 CPU 클럭에 달려있다.
클럭이 많으면 더 많은 일을, 적으면 더 적은 일을 한다.
또한 CPU 비트 수에 달려있다.
64비트는 8바이트씩 처리해서 더 많은 일을, 32비트는 4바이트씩 처리해서 더 적은 일을 하게된다.

즉, 우리가 루프를 몇번 걸었냐에 따라 시간이 얼마나 걸릴지를 알 수 없다는 것이다.
머신마다 처리능력이 다르기 때문이다.
1억번 루프를 하더라도 어떤건 0.5초만에 어떤 CPU는 5분만에 처리할 수도 있다.
그렇기 때문에 루프가 길어지면 블로킹으로 작동하게 하면 안된다.

블로킹을 주기적으로 풀어줘야한다.


어떻게?

총 1억번 루프라면 1000번 루프를 10만번 실행하는 것이다.
하지만 루프가 끝나고 다시 실행한다면 여전히 블로킹이다.
그렇기 때문에 사이사이마다 슬립을 걸어줘야된다.

슬립이란 CPU를 다른 애들이 사용할 수 있게 풀어준다는 개념이다.
자바스크립트는 이런 행위를 프레임이란 개념으로 할 수 있다.

  • 노드 - nextTick
  • 브라우저 - requestAnimationFrame, setTimeout

자바스크립트만 이런 긴 블로킹을 못 갖는게 아니다.
자바, C 등등 다 마찬가지이다.
어떤 프로그래밍 언어로 만들어진 명령어든 OS가 더 강하기 때문에 OS가 바로 중지시켜버린다.

“이렇게 블로킹 일으키면 안돼!”

왜 이런식으로 바뀌어가는 걸까?

안드로이드 게임을 개발했는데, 이 게임이 데이터처리한다고 5분을 붙잡고 있다고 해보자.
그런데 그 사이에 전화가 왔다고 해보자.
그런데 데이터 처리한다고 전화를 못받는 상황이라면?
이런 경우를 허용할 수 있느냐는 거다.
전화오면 OS가 해당 앱을 중지시켜야된다는 뜻이다.
그래서 OS의 힘이 커지는 것이다.

이러한 이유로 요즘엔 프로세스의 권한을 축소해 마음대로 할 수 있는 OS가 탄생하는 것이다.

옛날 바이너리 C코드들은 거의 머신 자원을 자기네들이 독점했다.
해당 프로세스들이 중지되면?
바로 블루스크린이 뜨는 것이다.
이렇듯 예전엔 OS를 중지시킬 정도로 앱이 강력한 권한을 가졌었다.
하지만 지금은 앱만 중지되지 안드로이드 OS가 통채로 뻗진 않는다.
OS가 더 많은 권한을 가지고 있어서 그렇다.

이러한 기저에 있어서 우리는 루프를 함부로 길게 만들 수 없다.

그런데 그정도로 긴 루프가 있나요? 이런 걱정을 할 정도로?

canvas tag를 생각하면 된다.
100 X 100 이미지를 그레이스케일 씌워주려고 canvas tag를 활용해 반복문만 돌려도 벌써 10000번이다.
100 X 100이면 그리 크지도 않은 아이콘 사이즈이다.
1920 X 1000 이런 이미지는?
이런 이미지를 반복문으로 돌리면 브라우저가 중지시키는 것이다.
성능이 나쁜 PC에서하면 OS가 브라우저채로 중지시킨다.
그렇기 때문에 루프를 분산해서 거는 스킬이 필요하다.

루프 만들 때 좋은 팁 - 무조건 limit를 걸어라

while(true)가 아닌 while(true && i<7) 이렇게 정의해야된다.
i는 바깥에다 정의하고..
우리가 만드는 루프에는 이런 안전장치가 무조건 있어야 된다.

iterable iterator pattern을 쓸 때 중요한 점 : done이 무한히 false가 되지 않게 하는 것
이걸 못하면 여러분들의 앱이나 웹사이트는 수시로 죽게된다.

const N2 = class {
    constructor(max) {
        // max 설정 이유 : 이 배열이 무한배열이 되는걸 막아주기 위해 만든 것
        this.max = max;
    }
    [Symbol.iterator](){
        let cursor = 0, max = this.max;
        return {
            done: false,
            // next 함수 태어나는 시점
            // 태어나는 시점에서 위의 curser, max같은 자율변수를 캡쳐에 가둘 수 있다.
            // 우리가 흔히 아는 클로져라는 개념이다.
            next() {
                if (cursor > max) {
                    this.done = true;
                }else {
                    this.value = cursor * cursor;
                    cursor++;
                }   
                return this;
            }
        }
    }
}

console.log(...new N2(5)); // 0 1 4 9 16 25
for (const v of new N2(5)) console.log(v)
// 0
// 1
// 4
// 9
// 16
// 25

함수는 함수가 만들어질 때 함수의 바깥에 있는 변수들을 캡쳐하는 기능이 있다.
캡쳐해서 마치 자기들의 지역변수처럼 사용하는 것이다.
이를 free variable이라 부른다.

자기들의 인자 값도 아니고, 지역변수도 아닌데 사용할 수 있는 변수를 자율변수(free variable)이라한다.
이런 자율변수가 잡혀서 함수가 해당 자율변수를 사용할 수 있는 닫혀진 공간을 클로저라고 부른다.
즉, 함수 = 클로저이기도 하다. 자율변수를 가둬둘 수 있기 때문이다.

함수라는 것은 원래 인자와 지역변수만을 쓸 수 있는데, 그게 아닌 뭔가를 쓸 수 있으면 그건 모두 자율변수고 그 자율변수가 갇히면 그게 클로저라는 것이다.
바깥쪽 레벨도 인식 가능하다.
위의 소스에서 Symbol.iterator라던지 N2도 인식 가능하다. - 스코프 체인

그런데 iterable, iterator interface 좋긴한데 소스가 너무 길다.
이걸 좀 더 줄여서 쓸 수는 없나?