munifico.github.io

munifico's anything page

Follow me on GitHub

결정되지 않는 프라미스 방지하기

프라미스는 비동기적 코드를 단순화하고 콜백이 두 번 이상 실행되는 문제를 방지합니다. 하지만 resolve나 reject를 호출하는 걸 잊어서 프라미스가 결정되지 않는 문제까지 자동으로 해결하지는 못합니다. 에러가 일어나지 않으므로 이런 실수는 찾기 매우 어렵습니다. 복잡한 시스템에서 결정되지 않은 프라미스는 그냥 잊혀질 수 있습니다.

결정되지 않은 프라미스를 방지하는 한 가지 방법은 프라미스에 타임아웃을 거는 겁니다. 충분한 시간이 지났는데도 프라미스가 결정되지 않으면 자동으로 실패하게 만들 수 있습니다. 물론 얼마나 기다려야 ‘충분히’ 기다렸는지는 스스로 판단해야 합니다. 10분 정도는 걸릴 거로 생각하는 복잡한 알고리즘에 1초 타임아웃을 걸어서는 안됩니다.

launch 함수에 에러 조건을 넣어 봅시다. 발사하는 로켓은 열 번에 다섯 번은 실패하는 매우 실험적인 로켓입니다.

function launch() {
    return new Promise((resolve, reject) => {
        if (Math.random() < 0.5) return; //문제 발생!
        console.log('Lift off!');
        setTimeout(() => {
            resolve('In orbit!');
        }, 2*1000);
    });
}

이 코드는 정말 무책임합니다. reject를 호출하지 않는데다가, 심지어 콘솔에 기록하지도 않습니다. 열 번 시도하면 그중 다섯 번은 영문도 모른 채 실패하는 셈입니다. 절대 바람직한 일이 아니죠.

다음과 같이 프라미스에 타임아웃을 거는 함수를 만들 수 있습니다.

function addTimeout(fn, timeout) {
    if (timeout === undefined) timeout = 1000; //타임아웃 기본값
    return function(...args) {
        return new Promise(function(resolve, reject) {
            const tid = setTimeout(reject, timeout, 
                new Error('promise timed out'));
            fn(...args)
                .then(function(...args) {
                    clearTimeout(tid);
                    resolve(...args);
                })   
                .catch(function(...args) {
                    clearTimeout(tid);
                    reject(...args);
                }); 
        });
    }
}

“와… 프라미스를 반환하는 함수를 호출하는 프라미스를 반환하는 함수를 반환하는 함수? 뭐가 이리 복잡해!” 하고 외치더라도 충분히 이해할 수 있습니다. 프라미스에 타임아웃을 걸기 위해서는 함수를 반환하는 함수가 필요한데, 절대 쉬운 문제는 아닙니다. 이 함수를 완벽히 이해하는 건 상당한 고급 사용자에게나 가능한 일이니 당장 이해하지 못해도 괜찮습니다. 하지만 이 함수를 사용하는 건 아주 쉽습니다. 프라미스를 반환하는 어떤 함수에든 타임아웃을 걸 수 있습니다. 로켓 과학이 엄청나게 발달해서, 가장 느린 로켓도 10초 안에는 궤도에 들어갈 수 있다고 합시다. 그러면 타임 아웃은 11초면 충분합니다.

const p = new Countdown(5, true);
p.on('tick', i => console.log(i + '...'));    
p.go()
    .then(addTimeout(launch, 11*1000))
    .then(msg => console.log(msg))
    .catch(err => console.error('Houston, we have a problem...'));

/*
5...
4...
3...
2...
1...
0...
//에러 발생 !! 11초 후...
Houston, we have a problem...
*/    

이제 launch 함수에 문제가 있더라도 프라미스 체인은 항상 결정됩니다.


이전 : 프라미스 체인
다음 : 제너레이터
목차