yield pattern
yield 패턴에 대해서 알아보겠습니다.
정의
yield 키워드는 제너레이터 함수 (function* 또는 레거시 generator 함수)를 중지하거나 재개하는데 사용됩니다.
함수 실행 중 yield 를 만나면 결과를 리턴하고 함수는 종료되지 않고 대기 합니다.
문법 및 동작
yield를 사용하려면 함수뒤에 를 붙여야 합니다. 즉, function 로 표기하여야 합니다.
일반적으로 yield 함수를 generator에 넣고, next()
를 호출하여 함수를 수행합니다.
이 때, 함수는 yield 를 만날 때까지 수행되며, yield를 만나면 결과를 리턴합니다.
이 때, next()
를 다시 호출하면, 함수는 대기한 그 다음 구문부터 실행한다. 시작은 대기 이후부터 실행하나 그 이전에 선언된 변수나 처리된 값도 모두 적용됩니다.
function* add() {
let a = 1;
let b = 2;
yield a + 2;
yield b + 2;
}
var generator = add(1);
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
// {value: 3, done: false}
// {value: undefined, done: true}
yield로 나온 결과는 value와 done으로 json형태로 표현됩니다.
done는 함수가 모두 종료되거나 return을 만나면 true
로 표기됩니다.
promise와 비교
javascript는 비동기이기 때문에 함수 실행 도중 다른 함수의 실행 결과를 대기할 수 없습니다.
이를 극복하기 위해 promise와 async await을 사용하였습니다.
let result = await add(3,5);
console.log(result);
function add(a,b) {
return new Promise((resolve, reject) => {
resolve(a+b);
});
}
promise를 사용하면 함수가 정리가 잘 되지만 함수가 복잡해져 읽기가 쉽지 않습니다.
그러나 이를 yield 를 활용하면 조금 더 간결해질 수 있습니다.
let generator = add(3,5);
console.log(generator.next());
console.log(generator.next());
function* add(a,b) {
yield a+b;
}
// {value: 3, done: false}
// {value: undefined, done: true}
단점이라면 선언부와 실행부가 나뉘므로 문장을 읽기는 쉬우나 이해하기는 쉽지 않을 수 있습니다.
callback hell, promise hell의 개선
콜백헬은 이제 유명한 문제입니다.
function cadd2(val, callback) {
return callback(val + 2);
}
console.log(cadd2(1, function (val) {
return val + 4;
}));
이에 대한 대안으로 promise를 활용하고 있습니다.
그러나 promise의 결과를 또 다른 promise에 반영하다보면 then 안에 또 다른 promise가 반복되면서 promise hell이 되는 또 다른 문제가 발생합니다.
function add2(val) {
return new Promise((resolve, reject) => {
resolve(val + 2);
});
}
function add4(val) {
return new Promise((resolve, reject) => {
resolve(val + 4);
});
}
add2(1).then(result => {
add4(result).then(final => {
console.log(final);
});
});
이를 yield를 활용하여 매우 깔끔하게 정리할 수 있습니다.
function* add2(val) {
yield val + 2;
}
function* add4(val) {
yield val + 4;
}
var generator = add2(1);
var result = generator.next();
var generator2 = add4(result.value);
console.log(generator2.next());
yield와 return
yield 함수에서 함수를 모두 마치지 않고 중간에 return을 만나면 어떻게 될까요?
결론을 말하자면 return을 만나는 순간 done이 true
가 되며, 그 이후의 next는 허용되지 않으며, 종료된 함수로 인식합니다.
let generator = add(3,5);
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
function* add(a,b) {
yield a+b+0;
return a+b+1;
yield a+b+2;
}
// {value: 8, done: false}
// {value: 9, done: true}
// {value: undefined, done: true}
yield*
만일 yield에 또 다른 yield 함수를 실행하려면 yield*
를 활용하여야 하며, 그렇지 않으면 에러가 발생합니다.
function* add(val) {
yield* add4(val + 2);
}
function* add4(val) {
yield val + 4;
}
var generator = add(1);
console.log(generator.next());
console.log(generator.next());
// {value: 7, done: false}
// {value: undefined, done: true}
예제를 실행해보면 yield -> yield
이므로 next() -> next()
를 해야할 것 같지만, yield* 이므로 next() 한번에 실제 yield를 만날 때까지 수행되어 한번에 실행됨을 확인할 수 있습니다.
결론
yield는 비동기에서 promise 외의 또 다른 동기 실행 방식이며, 함수를 중간에 대기시킬 수 있다는 점은 promise보다 더 나은 기능이라고 볼 수 있습니다.
그러나 남발하면 눈에는 쉬우나 이해하기 어려운 코드가 될 수 있으므로 적절한 조절이 필요할 것 같습니다.
댓글남기기