- μ΄ κΈμ The Hidden Power of ES6 Generators: Observable Async Flow Controlλ₯Ό λ²μν κΈμ λλ€.
- μμ§ μ λ¬Έμμ΄λ€λ³΄λ μ€μμ ν κ²½μ°κ° μμ μ μμ΅λλ€. μν΄ λΆνλ립λλ€.
- 맀λλ¬μ΄ λ¬Έλ§₯μ μνμ¬ μμμ ν κ²½μ°κ° μμ΅λλ€. μλ¬Έμ λ»μ μ΅λν ν΄μΉμ§ μλλ‘ λ Έλ ₯νμΌλ μμ¬νμ λ λ©λλ€.
- μμ΄ λ¨μ΄κ° μμ°μ€λ¬μ΄ κ²½μ° μλ¬Έ κ·Έλλ‘μ μλ¨μ΄λ₯Ό μ μμ΅λλ€.
- μ μ 보좩 μ€λͺ μ μΈμ©λ¬Έμ λ¬μμ΅λλ€.
νΌλ³΄λμΉ μ λλ μ΄ν°λ₯Ό μλ°μ€ν¬λ¦½νΈλ‘ μμ±νλ©° κΉ¨λ¬μ λλΌμ΄ μ 7κ°μ§μμλ ES6 μ λλ μ΄ν° ν¨μμ νμ€ν μ¬μ©λ‘λ₯Ό λ€λ€μ΅λλ€. νλ²μ νλμ© λ°λ³΅ν μ μλ(Iterable) κ°λ€λ‘ μ΄λ£¨μ΄μ§ μμ΄μ λ§λλ κ²μ΄μλλ°μ. μμ§ κ·Έ κΈμ μ½μ§ μμΌμ ¨λ€λ©΄, λ°λμ μ½μΌμ μΌ ν©λλ€! λ°λ³΅ κ°λ₯ν κ°μ²΄λ ES6+λ₯Ό ꡬμ±νλ λ€μν κΈ°λ₯λ€μ λ°νμ μ΄λ£¨κ³ μκΈ° λλ¬Έμ, μ΄λ»κ² μλνλμ§λ₯Ό κΌ μ΄ν΄νκ³ μμ΄μΌ ν©λλ€.
νμ§λ§ κ·Έ κΈμμλ μΌλΆλ¬, μ λλ μ΄ν°μ κ°μ₯ μ£Όμν μ¬μ© μ¬λ‘λ₯Ό λ€λ£¨μ§ μμμ΅λλ€. κ°ν λ§ν건λ°, κ·Έκ²μ λ°λ‘ λΉλκΈ° νλ¦ μ μ΄μ λλ€.
μμ§ μλ°μ€ν¬λ¦½νΈ 곡μ νμ€ κΈ°λ₯μΌλ‘ μ±νλμ§λ μμμ§λ§, async/awaitμ λνμ¬ λ€μ΄λ³΄μ ¨μ κ²λλ€. ES6μμλ λͺ»νκ³ , ES2016μμλ λͺ»ν κ²μ΄κ³ , ES2017μμλ λ μ§λ λͺ¨λ¦ λλ€. κ·Έλ¦¬κ³ μ°λ¦¬ μΌλ° μ¬μ©μλ€μ μ€μ§μ μΌλ‘ μ¬μ©ν μ μλλ‘ λκΈ° μ μ, λͺ¨λ μλ°μ€ν¬λ¦½νΈ μμ§λ€μ΄ ꡬννκΈ°λ₯Ό κΈ°λ€λ €μΌλ§ νμ£ . (μ°Έκ³ : μ§κΈμ Babelμμ μ¬μ© κ°λ₯νμ§λ§, κ³μ μ₯λ΄ν μλ μμ΅λλ€. 꼬리 νΈμΆ μ΅μ νκ°μ κ²½μ° λͺ κ°μ μ λ§ νλλΌλ Babelμμ μ¬μ© κ°λ₯νμμ§λ§ κ²°κ΅μ μ κ±°λμμ΅λλ€.)
μλ¬Έμ΄ 2016λ 5μ 21μΌμ μμ±λμμΌλ―λ‘, μλ¬Έ μμ± λΉμ μ΅μ μλ°μ€ν¬λ¦½νΈ λ²μ μ ES6μ΄μκ² κ΅°μ. νμ€ μλ°μ€ν¬λ¦½νΈ(ES)μ μ΅μ κ°μ μ μ ν΅μ μΌλ‘ 맀λ 6μμ μ΄λ£¨μ΄μ§λλ€.
μ΄λ° κΈ°λ€λ¦Όμλ λΆκ΅¬νκ³ , async/awaitμ λνμ¬ λ€λ£¨λ κΈμ νμ¬ μλΉν λ§μ΅λλ€. μμΌκΉμ?
μλμ κ°μ μ½λλ₯Ό:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
const promiseFunc = () => new Promise((resolve) => {
fetchSomething().then(result => {
resolve(result + ' 2');
});
});
promiseFunc().then(res => console.log(res));
μλμ κ°μ΄ λ°κΏμ£ΌκΈ° λλ¬Έμ λλ€:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
async function asyncFunction() {
const result = await fetchSomething(); // νλΌλ―Έμ€λ₯Ό λ°ν
// νλΌλ―Έμ€λ₯Ό κΈ°λ€λ Έλ€κ°, νλΌλ―Έμ€μ ν΄κ²°λ κ²°κ³Όλ₯Ό μ¬μ©
return result + ' 2';
}
asyncFunction().then(result => console.log(result));
첫λ²μ§Έ μ½λμμ νλΌλ―Έμ€ κΈ°λ°μ ν¨μκ° μ€μ²©μ ν λ¨κ³ λ λ§λ€μλ€λ μ μ μ£Όλͺ©νμκΈ° λ°λλλ€. async/await λ²μ μ μΌλ°μ μΈ λκΈ° μ½λμ²λΌ 보μ΄μ§λ§, μ¬μ€μ κ·Έλ μ§ μμ΅λλ€. νλΌλ―Έμ€λ₯Ό μ°μΆ(yield)νκ³ ν¨μλ₯Ό μ μ λ©μΆμ΄μ μλ°μ€ν¬λ¦½νΈ μμ§μ΄ λ§μ λκ³ λ€λ₯Έ μμ
μ ν μ μλλ‘ λμμ€λλ€. κ·Έλ¦¬κ³ fetchSomething()
μ΄ μ°μΆν νλΌλ―Έμ€κ° κ°μ μ μμ μΌλ‘ κ°μ Έμλ€λ©΄(resolve
μ μ€ν), μκΉ λ©μ·λ ν¨μκ° λ€μ λμμ μ¬κ°νκ³ , νλΌλ―Έμ€μ ν΄κ²°λ κ²°κ³Ό κ°μ result
λ³μμ ν λΉλ©λλ€.
λ§μΉ λκΈ°μ μΌλ‘ 보μ΄λ λΉλκΈ°μ μΈ μ½λμ λλ€. λ§€μΌ μ μ μμ΄ λ§μ λΉλκΈ° νλ‘κ·Έλλ°μ νλ μλ°μ€ν¬λ¦½νΈ νλ‘κ·Έλλ¨Έμκ² μ΄κ²μ κ·ΈμΌλ§λ‘ μ±λ°° κ·Έ μ체μ λλ€. μΈμ§μ μΈ κ³ΌλΆνκ° μ ν μμ΄ λΉλκΈ° μ½λμ μ±λ₯μ μ΄μ μ λͺ¨λ μ·¨ν μ μκΈ° λλ¬Έμ λλ€.
μλ¬Έμ
yield
λμ°μΆ
λ‘ λ²μνμμ΅λλ€. λν μλ¬Έμμresolve
λν΄κ²°
λ‘ λ²μνμμ΅λλ€.
μ¬κΈ°μ μ’ λ μμλ³΄κ³ μ νλ κ²μ λ°λ‘ async/awaitκ° λ°°νμμ μ΄λ»κ² μ λλ μ΄ν°λ₯Ό νμ©νλμ§... κ·Έλ¦¬κ³ , async/awaitκ° λΉμ₯ μλ μ§κΈ, λκΈ°μ μΈ μ€νμΌλ‘ νλ¦ κ΄λ¦¬λ₯Ό ν λμ μ λλ μ΄ν°λ₯Ό μ΄λ»κ² νμ©ν μ μλμ§μ λν κ²μ λλ€.
μ λλ μ΄ν° ν¨μλ ES6μ μλ‘μ΄ κΈ°λ₯μΌλ‘, μ΄ ν¨μλ κ³μ λ°λ³΅λ μ μλ κ°μ²΄λ₯Ό λ°νν¨μΌλ‘μ¨ μκ°μ΄ μ§λ¨μ λ°λΌ κ³μν΄μ μ¬λ¬ κ°λ€μ μμ±ν΄λΌ μ μμ΅λλ€. μ¬κΈ°μ λ°νλλ κ°μ²΄λ λ°λ³΅ κ°λ₯ν κ°μ²΄λ‘, μ΄ κ°μ²΄λ .next()
λ©μλλ₯Ό νΈμΆνλ©΄ μλμ κ°μ κ°μ²΄λ₯Ό λ°νν©λλ€:
{
value: Any,
done: Boolean
}
- μ λλ μ΄ν° ν¨μ νΈμΆ =λ°ν=> λ°λ³΅μ κ°μ²΄ =
.next()
νΈμΆ=> { value, done }- ν λ°λ³΅μμ λνμ¬ λ°λ³΅μ μΌλ‘
.next()
νΈμΆ κ°λ₯
done
μμ±μ μ λλ μ΄ν°κ° λ§μ§λ§ κ°μ μ°μΆν΄λλμ§ μ¬λΆλ₯Ό μλ €μ€λλ€.
λ°λ³΅μ νλ‘ν μ½μ μλ°μ€ν¬λ¦½νΈμ λ€μν κ²λ€μμ μ¬μ©λκ³ μλλ°, κ·Έ μ€μλ for...of
λ°λ³΅λ¬Έ, λ°°μ΄μ ... μ°μ°μ λ±μ΄ ν¬ν¨λ©λλ€.
function* foo() {
yield 'a';
yield 'b';
yield 'c';
}
for (const val of foo()) {
console.log(val);
}
// a
// b
// c
const [...values] = foo();
console.log(values); // ['a','b','c']
μ΄μ λΆν° μ¬λ―Έμμ΄μ§λλ€. μ λλ μ΄ν°μμ ν΅μ μ μλ°©ν₯μΌλ‘ μ΄λ£¨μ΄μ§λλ€. μ λλ μ΄ν°λ‘λΆν° κ°μ λ°λ κ²λΏ μλλΌ, μ λλ μ΄ν° ν¨μμ κ°μ μ λ¬ν μλ μμ΅λλ€. μ λλ μ΄ν°μ κ°μ μ λ¬νλ €λ©΄, λ°λ³΅μμ .next()
λ©μλμ μΈμλ₯Ό μ¬μ©ν©λλ€.
function* crossBridge() {
const reply = yield 'What is your favorite color?';
console.log(reply);
if (reply !== 'yellow') return 'Wrong!'
return 'You may pass.';
}
{
const iter = crossBridge();
const q = iter.next().value; // λ°λ³΅μκ° μ§λ¬Έμ μ°μΆνλ€
console.log(q);
const a = iter.next('blue').value; // λ΅λ³μ μ λλ μ΄ν°λ‘ λ€μ μ λ¬νλ€
console.log(a);
}
// What is your favorite color?
// blue
// Wrong!
{
const iter = crossBridge();
const q = iter.next().value;
console.log(q);
const a = iter.next('yellow').value;
console.log(a);
}
// What is your favorite color?
// yellow
// You may pass.
[λͺ¬ν° νμ΄νΌμ μ±λ°° δΈ μ£½μμ λ€λ¦¬]
μ λλ μ΄ν°μ ν΅μ νλ λ°©λ²μ λͺ κ°μ§ λ μ‘΄μ¬ν©λλ€. μ€λ₯λ₯Ό λμ§λ κ²λ νλμ λ°©λ²μ
λλ€. .next()
λ₯Ό νΈμΆνλ λμ , μλ₯Ό λ€μ΄ iterator.throw(error)
λ₯Ό νΈμΆνλ©΄ μ λλ μ΄ν°μμ μ¬μ©ν λ°μ΄ν°λ₯Ό κ°μ Έμ€λ λ°μ 무μΈκ° λ¬Έμ κ° μμμμ μ릴 μ μμ΅λλ€. λν, iterator.return()
μ νΈμΆνμ¬ μ λλ μ΄ν°κ° κ°μ λ‘ κ²°κ³Όλ₯Ό λ°ννλλ‘ λ§λ€ μλ μμ΅λλ€.
λ λ°©λ² λͺ¨λ νλ¦ μ μ΄ μ½λμ μ€λ₯ μ²λ¦¬ κΈ°λ₯μ μ μ©νλ κ°νΈν λ°©λ²μ λλ€.
κ·Έλ λ€λ©΄ μ΄λ° ν¨μλ μ΄λ€κ°μ? μλ‘μ΄ νλΌλ―Έμ€κ° μμ±λλ©΄ μ΄λ₯Ό κ°μ§νκ³ , μ΄ νλΌλ―Έμ€κ° κ°μ μ μμ μΌλ‘ κ°μ Έμ¬ λκΉμ§ λκΈ°ν λ€, ν΄κ²°λ κ°μ μ λλ μ΄ν°λ‘ μ ν΄μ£Όλ©΄μ .next()
λ₯Ό νΈμΆνλ ν¨μ λ§μ΄μ£ . μ΄ ν¨μλ μ λλ μ΄ν°λ₯Ό λννλ ννκ° λ κ²μ
λλ€.
μ΄λ° ν¨μλ₯Ό μ¬μ©νλ©΄, μλμ κ°μ async/await μ€νμΌμ μ½λλ₯Ό μμ±ν μ μκ² λ©λλ€:
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
const asyncFunc = gensync(function* () {
const result = yield fetchSomething(); // νλΌλ―Έμ€λ₯Ό λ°ν
// νλΌλ―Έμ€λ₯Ό κΈ°λ€λ Έλ€κ°, νλΌλ―Έμ€μ ν΄κ²°λ κ²°κ³Όλ₯Ό μ¬μ©
yield result + ' 2';
});
// asyncFuncλ₯Ό νΈμΆνλ©΄μ μΈμλ₯Ό μ λ¬νλ€.
asyncFunc('param1', 'param2', 'param3')
.then(val => console.log(val));
보μνλ, μ΄λ° κΈ°λ₯μ μννλ Co.jsλΌλ λΌμ΄λΈλ¬λ¦¬κ° μ΄λ―Έ μ‘΄μ¬ν©λλ€. νμ§λ§ Coμ μ¬μ©λ²μ κ°λ₯΄μ³λ리λ λμ , Coμ κ°μ κ²μ μ§μ λ§λ€μ΄λ³΄λλ‘ ν©μλ€. μμ crossBridge()
μμ λ₯Ό μ μ΄ν΄λ³΄λ©΄, κ·Έλ κ² μ΄λ ΅μ§ μμ 보μ
λλ€.
μ¬νν isPromise()
ν¨μλ‘λΆν° μμνλλ‘ νμ£ .
const isPromise = obj => Boolean(obj) && typeof obj.then === 'function';
λ€μμΌλ‘, .next()
λ₯Ό νΈμΆνμ¬ μ λλ μ΄ν° λ΄λΆλ₯Ό μννκ³ , νλΌλ―Έμ€κ° ν΄κ²°λ κ°μ κ°μ Έμ€κΈ°λ₯Ό κΈ°λ€λ¦° λ€ λ€μ .next()
λ₯Ό νΈμΆν μ μλ μλ¨μ΄ νμνκ² κ΅°μ. μλμ μ½λλ μ΄λ¬ν λμμ μννκ³ μμ΅λλ€. λ€λ§, μ€λ₯ μ²λ¦¬ κ³Όμ μ΄ μλ΅λ λ¨μ κ°λ
μ€λͺ
μ©λμ μ½λμ
λλ€. κ·Έλ¬λ―λ‘ μ€μ κ°λ°μμλ μ¬μ©νμ§ λ§μΈμ. μ€λ₯κ° λ¬»νλ²λ¦¬λ©΄, λλ²κΉ
νλ κ²μ΄ κ΅μ₯ν 골μΉμνμ§ κ²λλ€:
const next = (iter, callback, prev = undefined) => {
const item = iter.next(prev);
const value = item.value;
if (item.done) return callback(prev);
if (isPromise(value)) {
value.then(val => {
setImmediate(() => next(iter, callback, val));
});
} else {
setImmediate(() => next(iter, callback, value));
}
};
보μλ λ°μ κ°μ΄. μ΅μ’
κ²°κ³Όλ₯Ό λ°ννκΈ° μνμ¬ μ½λ°±μ μ λ¬νκ³ μμ΅λλ€. ν¨μμ μ΅μλ¨ λΌμΈμ 보면, λ°λ‘ μ§μ μ κ°μ .next()
νΈμΆμ μΈμλ‘ μ λ¬νλ κ²μΌλ‘ μ λλ μ΄ν°μ ν΅μ νκ³ μμ΅λλ€. μ΄λ κ² νλ©΄ μ§μ μ yield
λ‘ μ λ¬λ°μ κ²°κ³Όλ₯Ό λ€μ μ λλ μ΄ν°λ‘ 건λ€μ€ μ μμ£ .
gensync
ν¨μκ° λνν μ λλ μ΄ν° λ΄λΆμμfetchSomething
μ κ²°κ³Όκ°yield
λ₯Ό ν΅νμ¬next
ν¨μμ μ λ¬λ©λλ€. μ΄ κ°μiter.next(prev).value
μμ μ μ μμ§μ. μ΄ κ°μ νλΌλ―Έμ€μ΄λ―λ‘,.then()
μ ν΅νμ¬ ν΄λΉ νλΌλ―Έμ€μ κ²°κ³Όκ°next
κ° μ¬κ· νΈμΆλ λμ 3λ²μ§Έ μΈμ,val
λ‘ μ λ¬λ©λλ€. κ·Έλ¬λ©΄ λ€μnext
κ° μ€νλ λ,iter.next(prev)
λ₯Ό ν΅νμ¬ μ λλ μ΄ν°λ‘ μ λ¬λκ³ , μ λλ μ΄ν°μμλ κ·Έ λ€μyield
μ€νμ λ°λΌ2
κ° λ§λΆμ¬μ§ κ²°κ³Όλ₯Όnext
μ λλ €μ£Όκ² λκ² μ§μ.
const next = (iter, callback, prev = undefined) => {
// 2. μ°μΆλ κ°μ `.next()`μ νΈμΆμ ν΅νμ¬ μΆμΆλλ€.
// μ§μ μ κ°μ μ λλ μ΄ν°μ μ λ¬νκ² λκ³ ,
// μ΄ κ°μ μ λλ μ΄ν° λ΄λΆμμ `result` λ³μμ κ° ν λΉμ μ¬μ©λλ€.
const item = iter.next(prev);
const value = item.value;
// 4. μ΅μ’
κ²°κ³Όκ°μ μ½λ°±μΌλ‘ μ λ¬λλ€.
if (item.done) return callback(prev);
if (isPromise(value)) {
value.then(val => {
setImmediate(() => next(iter, callback, val));
});
} else {
setImmediate(() => next(iter, callback, value));
}
};
const asyncFunc = gensync(function* () {
// 1. μ°μΆλ κ°μ΄ λ°λ³΅μλ‘ μ λ¬λλ€.
// yieldλ₯Ό λ§λ¬μ λμ μ λλ μ΄ν° ν¨μλ μΌμμ μΌλ‘ μ’
λ£λκ³ ,
// `result` λ³μμ κ°μ ν λΉνλ μμ
μ
// μ λλ μ΄ν°κ° λ€μ μ¬κ°λκΈ° μ κΉμ§ μ΄λ£¨μ΄μ§μ§ μλλ€.
const result = yield fetchSomething();
// 3. `.next()`κ° νΈμΆλκΈ° μ κΉμ§λ λ€μ μλνμ§ μλλ€.
// `result`λ λ°λ‘ μ§μ `.next()` νΈμΆμ μ λ¬λ κ°μ ν¬ν¨νκ² λλ€.
yield result + ' 2';
});
λ¬Όλ‘ , μ§μ ν¨μλ₯Ό μ€νμν€κΈ° μ κΉμ§λ μ무 μΌλ μΌμ΄λμ§ μμ΅λλ€. κ·Έλ¦¬κ³ , μ€μ μ΅μ’ κ°μ λ°ννλ νλΌλ―Έμ€λ μ΄λ μμκΉμ?
// νλΌλ―Έμ€λ₯Ό λ°ννκ³ , μ΅μ΄ `next()` νΈμΆμ ν΅νμ¬
// λͺ¨λ μμ
μ μλνλ€.
const gensync = (fn) =>
(...args) => new Promise(resolve => {
next(fn(...args), val => resolve(val));
});
gensync
μ μΈμλ‘ μ λ¬λfn
μ΄ μ λλ μ΄ν° ν¨μμ λλ€. 컀λ§(currying)μ΄ μ΄λ£¨μ΄μ Έ μμμ μ£ΌμνμΈμ.
μ, μ΄μ μ λΆ λͺ¨μλ΄ μλ€. μ€μ μ¬μ©νλ μ½λλ₯Ό μ μΈνλ©΄ 22μ€ λ¨μ§μ μ½λλ‘κ΅°μ.
const isPromise = obj => Boolean(obj) && typeof obj.then === 'function';
const next = (iter, callback, prev = undefined) => {
const item = iter.next(prev);
const value = item.value;
if (item.done) return callback(prev);
if (isPromise(value)) {
value.then(val => {
setImmediate(() => next(iter, callback, val));
});
} else {
setImmediate(() => next(iter, callback, value));
}
};
const gensync = (fn) =>
(...args) => new Promise(resolve => {
next(fn(...args), val => resolve(val));
});
/* How to use gensync() */
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
const asyncFunc = gensync(function* () {
const result = yield fetchSomething(); // νλΌλ―Έμ€λ₯Ό λ°ν
// νλΌλ―Έμ€λ₯Ό κΈ°λ€λ Έλ€κ°, νλΌλ―Έμ€μ ν΄κ²°λ κ²°κ³Όλ₯Ό μ¬μ©
yield result + ' 2';
});
// asyncFuncλ₯Ό νΈμΆνλ©΄μ μΈμλ₯Ό μ λ¬νλ€.
asyncFunc('param1', 'param2', 'param3')
.then(val => console.log(val)); // 'future value 2'
μ΄ κΈ°λ²μ μ€μ μ½λμ μ μ©νκ³ μΆλ€λ©΄, λΉμ°ν Co.jsλ₯Ό λμ μ¬μ©ν΄μΌ ν©λλ€. μ΄ λΌμ΄λΈλ¬λ¦¬μλ μ€λ₯ μ²λ¦¬ λ‘μ§μ΄ μ μ©λμ΄ μκ³ (μμ μμμμλ μ½λκ° κΈΈμ΄μ§λ κ²μ λ°©μ§νκ³ μ μ μΈνμ΅λλ€), λ°°ν¬ μμ€μ ν μ€νΈκ° μλ£λμμΌλ©°, κ·Έ μΈμ μ’μ κΈ°λ₯λ€μ΄ ν¬ν¨λμ΄μμ΅λλ€.
μμ μμλ μμ£Ό ν₯λ―Έλ‘κ³ , Co.jsλ λΆλͺ λΉλκΈ° νλ¦ μ μ΄λ₯Ό λ¨μνν΄μ£Όλ μ’μ λΌμ΄λΈλ¬λ¦¬μ λλ€. λ€λ§, νκ°μ§ λ¬Έμ κ° μμ΅λλ€. λ°νκ°μ΄ νλΌλ―Έμ€λΌλ κ²μ λλ€. λ€λ€ μμλ€μνΌ, νλΌλ―Έμ€λ νλμ λ°νκ° λλ κ±°μ (rejection)λ§μ λ§λ€μ΄λΌ μ μμ΅λλ€.
μ λλ μ΄ν°λ μκ°μ΄ μ§λ¨μ λ°λΌ λ§μ κ°λ€μ λ§λ€μ΄λΌ μ μμ΅λλ€. μκ°μ΄ μ§λ¨μ λ°λΌ λ§μ κ°λ€μ λ§λ€μ΄λΌ μ μλ κ², μ°λ¦¬κ° μκ³ μλ λ€λ₯Έ κ² λ μλμ? λ°λ‘ μ΅μ λ²λΈ(Observable)μ λλ€. νΌλ³΄λμΉ μ λλ μ΄ν°λ₯Ό μλ°μ€ν¬λ¦½νΈλ‘ μμ±νλ©° κΉ¨λ¬μ λλΌμ΄ μ 7κ°μ§λ₯Ό λ€μ λ μ¬λ €λ³΄μΈμ:
μ²μμλ μ λλ μ΄ν°μ λν κ²μ΄ κ΅μ₯ν λ§μμ λ€μμ§λ§, μ‘°κΈ μ¬μ©ν΄λ³΄κ³ λλ, μ€μ μ μ΄ν리μΌμ΄μ μ½λ μμμ μ λλ μ΄ν°λ₯Ό μ νμ©ν μ¬μ©λ‘(use-case)λ₯Ό μ°ΎκΈ° νλ€μμ΅λλ€. μ λλ μ΄ν°κ° νμν λλΆλΆμ κ²½μ°λΌλ©΄ μ λ μ€νλ € RxJSλ₯Ό μ¬μ©νμ΅λλ€. APIκ° ν¨μ¬ νλΆνκΈ° λλ¬Έμ΄μ£ .
νλΌλ―Έμ€λ (μ λλ μ΄ν° ν¨μμ λ¬λ¦¬) λ¨ νλμ κ°λ§ λ§λ€μ΄λΌ μ μκ³ , μ΅μ λ²λΈμ (μ λλ μ΄ν° ν¨μμ²λΌ) κ³μν΄μ λ§μ κ°λ€μ λ§λ€μ΄λΌ μ μκΈ° λλ¬Έμ, async ν¨μλ₯Ό λ€λ£¨λ λ°μλ μ΅μ λ²λΈμ APIκ° νλΌλ―Έμ€λ³΄λ€ λ μ ν©νλ€λ κ²μ΄ κ°μΈμ μΈ μκ°μ λλ€.
μ΅μ λ²λΈμ΄λ 무μμΈκ°μ?
Singular | Plural | |
---|---|---|
Spatial | Value | Iterable |
Temporal | Promise | Observable |
μμ νλ Kris Kowalμ΄ μμ±ν κΈ GTOR: A General Theory of Reactivityμμ κ°μ Έμμ΅λλ€. λμμ λνμ¬ μκ°κ³Ό 곡κ°μ κΈ°μ€μΌλ‘ λΆν΄ν©λλ€. λκΈ°μ μΌλ‘ κ°μ Έμ¬ μ μλ κ°μ 곡κ°μ μλΉνμ§λ§(λ©λͺ¨λ¦¬ μμ κ°), μκ°μΌλ‘λΆν° λΆλ¦¬λμ΄μμ΅λλ€. μ΄λ¬ν κ°μ΄ Pull API μ λλ€.
λ―Έλμ νΉμ μ΄λ²€νΈμ μμ‘΄νλ κ°μ λκΈ°μ μΌλ‘ μ¬μ©λ μ μμ΅λλ€. μ¬μ©ν μ μκ² λκΈ° μ κΉμ§ ν΄λΉ κ°μ΄ μ²λ¦¬λκΈ°λ₯Ό κΈ°λ€λ €μΌ ν©λλ€. μ΄λ¬ν κ°μ Push API λ‘, μΈμ λ νΉμ ννμ ꡬλ λλ μλ¦Ό λ©μ»€λμ¦μ κ°μ§κ² λ©λλ€. μλ°μ€ν¬λ¦½νΈμμλ, μ½λ°± ν¨μ ννλ₯Ό 보μ΄λ κ²μ΄ 보ν΅μ λλ€.
λ―Έλμ κ°μ λ€λ£° λμλ, κ°μ΄ μ¬μ© κ°λ₯ν΄μ§ λμ μλ¦Όμ λ°λ κ²μ΄ νμν©λλ€. κ·Έκ²μ΄ Push μ λλ€.
νλΌλ―Έμ€λ, μ΄λ€ νλΌλ―Έμ€κ° ν΄κ²°λμκ±°λ κ±°μ λλ©΄μ λ¨μΌ κ²°κ³Όκ°μ λ°νν λμ, 미리 μ ν΄μ§ μΌλ ¨μ νλ‘κ·Έλ¨ μ½λλ₯Ό νΈμΆνλ Push λ©μ»€λμ¦μ λλ€.
μ΅μ λ²λΈμ νλΌλ―Έμ€μ λΉμ·νμ§λ§, μ΅μ λ²λΈμ μλ‘μ΄ κ°μ΄ μ¬μ©κ°λ₯ν΄μ§ λλ§λ€ νμ 미리 μ ν΄μ§ μΌλ ¨μ νλ‘κ·Έλ¨ μ½λλ₯Ό νΈμΆνλ©°, μκ°μ΄ μ§λ¨μ λ°λΌ λ§μ κ°λ€μ λ§λ€μ΄λΌ μ μμ΅λλ€.
μ΅μ λ²λΈμ μ£Όμ κΈ°λ₯μ .subscribe()
λ©μλλ‘, μΈ κ°μ§ μ½λ°±λ€μ μΈμλ‘ μ λ¬ν©λλ€:
onNext
κ° νΈμΆλμμ λμ λ€μ΄μ΄μ νΈμΆλ©λλ€. λ¨, μ€λ₯κ° λ°μν μ μ΄ μμ΄μΌ ν©λλ€.λ°λΌμ, λκΈ°μ μΈ μ€νμΌμ async ν¨μλ₯Ό μν μ΅μ λ²λΈ APIλ₯Ό ꡬννκ³ μΆλ€λ©΄, μμ μΈ μΈμλ€μ μ λ¬ν΄μΌ ν©λλ€. λ°λ‘ λ§λ€μ΄λ³΄λλ‘ ν©μλ€. μ, onError
λ μ μ λ€λ‘ λ―Έλ€λμ£ .
const isPromise = obj => Boolean(obj) && typeof obj.then === 'function';
const next = (iter, callbacks, prev = undefined) => {
const { onNext, onCompleted } = callbacks;
const item = iter.next(prev);
const value = item.value;
if (item.done) {
return onCompleted();
}
if (isPromise(value)) {
value.then(val => {
onNext(val);
setImmediate(() => next(iter, callbacks , val));
});
} else {
onNext(value);
setImmediate(() => next(iter, callbacks, value));
}
};
const gensync = (fn) => (...args) => ({
subscribe: (onNext, onError, onCompleted) => {
next(fn(...args), { onNext, onError, onCompleted });
}
});
/* How to use gensync() */
const fetchSomething = () => new Promise((resolve) => {
setTimeout(() => resolve('future value'), 500);
});
const myFunc = function* (param1, param2, param3) {
const result = yield fetchSomething(); // νλΌλ―Έμ€λ₯Ό λ°ν
// νλΌλ―Έμ€λ₯Ό κΈ°λ€λ Έλ€κ°, νλΌλ―Έμ€μ ν΄κ²°λ κ²°κ³Όλ₯Ό μ¬μ©
yield result + ' 2';
yield param1;
yield param2;
yield param3;
}
const onNext = val => console.log(val);
const onError = err => console.log(err);
const onCompleted = () => console.log('done.');
const asyncFunc = gensync(myFunc);
// asyncFuncλ₯Ό νΈμΆνλ©΄μ μΈμλ₯Ό μ λ¬νλ€.
asyncFunc('a param', 'another param', 'more params!')
.subscribe(onNext, onError, onCompleted);
// future value
// future value 2
// a param
// another param
// more params!
// done.
μ΄ μ΅μ’
λ²μ μ μ½λκ° κ°μ₯ λ§μμ λλκ΅°μ. κΈ°λ₯μ΄ λ λ€μν΄μ‘κΈ° λλ¬Έμ΄μ£ . μ¬μ€, λ무 λ§μμ λ€μ΄μ, μ€λ₯ μ²λ¦¬ κΈ°λ₯μ λν λ€ Ogenμ΄λΌκ³ μ΄λ¦μ λΆμμ΅λλ€. 무μ보λ€λ, μ§μ ν Rx Obervable κ°μ²΄λ€μ΄ κΈ°λ₯λ€μ μΆκ°νμ΅λλ€. λ΄λΆ μμλ€μ λνμ¬ .map()
, .filter()
, .skip()
λ±μ λΉλ‘―ν λ€μν κΈ°λ₯λ€μ μ¬μ©ν μ μμ΅λλ€.
Ogenμ κΈ°λ₯μ΄ κΆκΈνλ©΄ Githubλ₯Ό νμΈν΄μ£ΌμΈμ.
λΉλκΈ° νλ¦ μ μ΄λ₯Ό ν₯μμμΌμ£Όλ λ€μν μ΅μ λ²λΈ λΌμ΄λΈλ¬λ¦¬λ€μ΄ μ‘΄μ¬ν©λλ€. μ κ° μ λλ μ΄ν°λ₯Ό μ¬μ©ν΄μ€μ§ μμλ κ°μ₯ ν° μ΄μ μ΄κΈ°λ ν©λλ€λ§, μ΄μ λ λκΈ°μ μΈ μ€νμΌμ μ½λμ μ΅μ λ²λΈμ Ogenμ μ¬μ©νμ¬ μ μ νκ² νΌμ©ν λ― νκ΅°μ. μ΄μ©λ©΄, μ λλ μ΄ν°λ₯Ό μ μ λ μ¬μ©νκ² λ μ§λ λͺ¨λ₯΄κ² μ΅λλ€.