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
를 사용하는 것도 좋습니다.