LHJ

I'm a FE developer.

14.4.3 제너레이터 실행기와 예외 처리

21 May 2020 » js_lj

14.4.3 제너레이터 실행기와 예외 처리

제너레이터 실행기를 쓰면 try/catch를 써서 예외 처리를 할 수 있다는 것도 중요한 장점입니다.
콜백이나 프로미스를 사용하면 예외 처리가 쉽지 않습니다.
콜백에서 일으킨 예외는 그 콜백 밖에서 캐치할 수 없습니다.
제너레이터 실행기는 비동기적으로 실행하면서도 동기적인 동작 방식을 유지하므로 try/catch문과 함께 쓸 수 있습니다.
theFutureIsNow 함수에 예외 핸들러를 추가해 봅시다.

const fs = require('fs');

function nfcall(f, ...args) {
    return new Promise(function(resolve, reject) {
        f.call(null, ...args, function(err, ...args) {
            if(err) return reject(err);
            resolve(args.length < 2 ? args[0] : args);
        })
    })
}

function ptimeout(delay) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve, delay)
    })
}

function grun(g) {
    const it = g();
    (function iterate(val) {
        const x = it.next(val);
        if (!x.done) {
            if (x.value instanceof Promise) {
                x.value.then(iterate).catch(err => it.throw(err));
            } else {
                // 세번째 이후 매개변수 : 타이머가 만료되고 func에 전달되는 추가적인 매개변수들입니다.
                setTimeout(iterate, 0, x.value);
            }
        }
    })();
}

function* theFuntureIsNow() {
    let data;
    try {
        data = yield Promise.all([
            nfcall(fs.readFile, 'a.txt'),
            nfcall(fs.readFile, 'b.txt'),
            nfcall(fs.readFile, 'c.txt'),
        ])
    } catch (err) {
        console.error("Unable to read one or more input files: " + err.message);
        throw err;
    }
    yield ptimeout(60*1000);
    try {
        yield nfcall(fs.writeFile, 'd.txt', data[0] + data[1] + data[3]);
    } catch (err) {
        console.error("Unable to write output file: " + err.message);
        throw err;
    }
}

grun(theFuntureIsNow);

필자는 try/catch를 통한 예외 처리가 프로미스의 catch 핸들러나 오류 우선 콜백보다 낫다고 주장하는 건 아닙니다.
하지만 try/catch는 예외 처리에 널리 사용되고 다들 잘 이해하는 구조이니, 아직 동기적인 처리가 더 익숙하다면 예외 처리에 try/catch를 사용하는 것도 좋습니다.