Language/JavaScript

[JS] πŸ“š μ œλ„ˆλ ˆμ΄ν„° - μ΄ν„°λ ˆμ΄ν„° κ°•ν™”νŒ

인파_ 2021. 10. 6. 12:44

μ œλ„ˆλ ˆμ΄ν„°

 

μ œλ„€λ ˆμ΄ν„°λž€?

μ΄ν„°λŸ¬λΈ”μ΄λ©° λ™μ‹œμ— μ΄ν„°λ ˆμ΄ν„°

  = μ΄ν„°λ ˆμ΄ν„°λ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜

(asyncκ°€ Promiseλ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜λ“―μ΄, μ œλ„ˆλ ˆμ΄ν„°λŠ” μ΄ν„°λ ˆμ΄ν„°λ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜λ‹€.)

​

μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ μ΄ν„°λ ˆμ΄μ…˜ ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•΄ μ΄ν„°λŸ¬λΈ”μ„ μƒμ„±ν•˜λŠ” 방식보닀 κ°„νŽΈν•˜κ²Œ κ΅¬ν˜„ν•  수 μžˆλ‹€.

(Promise와 async 관계와 λΉ„μŠ·ν•˜κ²Œ 보면 λœλ‹€.)

​

​

μ΄ν„°λŸ¬λΈ” ν”„λ‘œν† μ½œ 방식

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    return {
      current: this.from,
      last: this.to,

      next() {
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// {from: 1, to: 5, Symbol(Symbol.iterator): ƒ}
// 이 κ°μ²΄λŠ” 인자λ₯Ό from, to λ‘κ°œ κ°€μ§€κ³ μžˆλŠ” κ·Έλƒ₯ 객체가 μ•„λ‹ˆλΌ,
// μ΄ν„°λ ˆμ΄ν„°κ°€ 적용된 νŠΉμˆ˜ν•œ 객체이닀. 이 객체λ₯Ό μ „κ°œμ—°μ‚°μž ν•  경우 μˆœνšŒλ˜μ–΄ λ‚˜νƒ€λ‚˜κ²Œλœλ‹€.
alert([...range]); // 1,2,3,4,5

​

μ œλ„ˆλ ˆμ΄ν„° 방식 1 (function μ˜†μ— *을 뢙인닀.)

const range = function* () { //μ œλ„ˆλ ˆμ΄ν„° 지정해주면 μ–˜ μžμ²΄κ°€ μ΄ν„°λ ˆμ΄ν„°λ₯Ό λ°˜ν™˜ν•΄μ€€λ‹€.
  let i = 1; 
  while(true){ //μ–΄μ°¨ν”Ό μ•ˆμ— yield에 μ˜ν•΄μ„œ μ½”λ“œκ°€ λ©ˆμΆ”λ‹ˆ λ¬΄ν•œλ£¨ν”„ ν•΄μ€˜μ„œ μ›ν• λ•Œ 진행을 μ΄μ–΄λ‚˜κ°ˆμˆ˜ 있게 ν•œλ‹€
    if (i <= 5)
      yield ++i; /* yieldλ₯Ό λ§Œλ‚˜λ©΄ μΌμ‹œμ •μ§€λ˜κ³ , 값을 건넀쀀닀. 그리고 for..of에 μ˜ν•΄μ„œ next()
                    κ°€ 호좜되면 ν•¨μˆ˜ 싀행을 이어 λ‚˜κ°„λ‹€. 
                    μ΄ν„°λŸ¬λΈ” 일경우 next()λ₯Ό μ •μ˜ν•˜κ³  μ•ˆμ— 리턴값을 {value:,donw:}을 일일히
                    μ •μ˜ ν•΄μ€˜μ•Ό ν•˜λŠ”λ°, μ œλ„ˆλ ˆμ΄ν„°λŠ” yield둜 ν‰μΉ μˆ˜ μžˆλ‹€. */
    else
      return;
  }
};


for(let i of range()){
  console.log(i); // 1,2,3,4,5
}

​

μ œλ„ˆλ ˆμ΄ν„° 방식 2 (심볼 μ΄ν„°λ ˆμ΄ν„° λ©”μ†Œλ“œμ— ν•œλ°© 정리)

let range = {
  from: 1,
  to: 5,

  *[Symbol.iterator]() { // [Symbol.iterator]: function*()λ₯Ό 짧게 μ€„μž„
    for(let value = this.from; value <= this.to; value++) {
      yield value;
    }
  }
};

alert( [...range] ); // 1, 2, 3, 4, 5

μ œλ„ˆλ ˆμ΄ν„° μ •μ˜

μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λŠ” 일반 ν•¨μˆ˜μ™€λŠ” λ‹€λ₯Έ λ…νŠΉν•œ λ™μž‘μ„ ν•œλ‹€.

μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λŠ”, 일반 ν•¨μˆ˜ 처럼 ν•¨μˆ˜μ˜ μ½”λ“œ 블둝을 ν•œ λ²ˆμ— μ‹€ν–‰ν•˜μ§€ μ•Šκ³  ν•¨μˆ˜ μ½”λ“œ λΈ”λ‘μ˜ 싀행을 μΌμ‹œ μ€‘μ§€ν–ˆλ‹€κ°€ ν•„μš”ν•œ μ‹œμ μ— μž¬μ‹œμž‘ν•  수 μžˆλŠ” νŠΉμˆ˜ν•œ ν•¨μˆ˜μ΄λ‹€.

// μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ 선언식
function* genDecFunc() {
  yield 1;
}
let generatorObj = genDecFunc(); // μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ μ‹€ν–‰ κ²°κ³Ό λ°˜ν™˜λœ μ œλ„ˆλ ˆμ΄ν„° 객체λ₯Ό λ³€μˆ˜μ— λ„£μ–΄ μ‚¬μš©ν•œλ‹€.


// μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ ν‘œν˜„μ‹
const genExpFunc = function* () {
  yield 1;
};
generatorObj = genExpFunc();


// μ œλ„ˆλ ˆμ΄ν„° λ©”μ†Œλ“œ 식
const obj = {
  * generatorObjMethod() {
    yield 1;
  }
};
generatorObj = obj.generatorObjMethod();


// μ œλ„ˆλ ˆμ΄ν„° 클래슀 λ©”μ†Œλ“œ 식
class MyClass {
  * generatorClsMethod() {
    yield 1;
  }
}
const myClass = new MyClass();
generatorObj = myClass.generatorClsMethod();

 

​

yield / next

yieldλŠ” μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜μ˜ 싀행을 μΌμ‹œμ μœΌλ‘œ μ •μ§€μ‹œν‚€λ©°, yield 뒀에 μ˜€λŠ” ν‘œν˜„μ‹μ€ μ œλ„ˆλ ˆμ΄ν„°μ˜ callerμ—κ²Œ λ°˜ν™˜λœλ‹€.

 

next λ©”μ†Œλ“œλŠ” { value, done } ν”„λ‘œνΌν‹°λ₯Ό κ°–λŠ” μ΄ν„°λ ˆμ΄ν„° 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

즉, value ν”„λ‘œνΌν‹°λŠ” yield 문이 λ°˜ν™˜ν•œ 값이고 done ν”„λ‘œνΌν‹°λŠ” μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ λ‚΄μ˜ λͺ¨λ“  yield 문이 μ‹€ν–‰λ˜μ—ˆλŠ”μ§€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” boolean νƒ€μž…μ˜ 값이닀.

 

λ§ˆμ§€λ§‰ yield λ¬ΈκΉŒμ§€ μ‹€ν–‰λœ μƒνƒœμ—μ„œ next λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ done ν”„λ‘œνΌν‹° 값은 trueκ°€ λœλ‹€.

function* generateSequence(){
   ...μ½”λ“œ
   yield 1; // 첫번째 호좜 μ‹œμ— 이 μ§€μ κΉŒμ§€ μ‹€ν–‰λœλ‹€.
   ...μ½”λ“œ
   yield 2; // λ‘λ²ˆμ§Έ 호좜 μ‹œμ— 이 μ§€μ κΉŒμ§€ μ‹€ν–‰λœλ‹€.

   return 3;
}

// μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ œλ„ˆλ ˆμ΄ν„° 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.
// μ œλ„ˆλ ˆμ΄ν„° κ°μ²΄λŠ” μ΄ν„°λŸ¬λΈ”μ΄λ©° λ™μ‹œμ— μ΄ν„°λ ˆμ΄ν„°μ΄λ‹€.
// λ”°λΌμ„œ Symbol.iterator λ©”μ†Œλ“œλ‘œ μ΄ν„°λ ˆμ΄ν„°λ₯Ό 별도 생성할 ν•„μš”κ°€ μ—†λ‹€
let iter = gen();

//μ‹€ν–‰ κ²°κ³Όκ°€ 자기 μžμ‹ μΈ Sysmbol.iteratorλ₯Ό 가지고 μžˆλ‹€. 
console.log(iter[Symbol.iterator]() == iter) // true

//value, done 이 μžˆλŠ” 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” nextλ₯Ό ν˜ΈμΆœν•˜λ©΄ μ΄ν„°λŸ¬λΈ” 객체λ₯Ό λ°˜ν™˜ν•˜κ³  ν•¨μˆ˜λŠ” μΌμ‹œμ€‘λ‹¨ λœλ‹€.
console.log(iter.next()); // { "value": 1, "done": false } + ν•¨μˆ˜ μ‹€ν–‰ 쀑단
console.log(iter.next()); // { "value": 2, "done": false } + ν•¨μˆ˜ μ‹€ν–‰ 쀑단

console.log(iter.next()); // { "value": 3, "done": true } + 순회 μ’…λ£Œ

μ œλ„ˆλ ˆμ΄ν„°
μ œλ„ˆλ ˆμ΄ν„°
μ œλ„ˆλ ˆμ΄ν„°

● μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ œλ„ˆλ ˆμ΄ν„° 객체λ₯Ό λ°˜ν™˜ν•œλ‹€. 근데 μ œλ„ˆλ ˆμ΄ν„° κ°μ²΄λŠ” μ΄ν„°λŸ¬λΈ”μ΄λ©° λ™μ‹œμ— μ΄ν„°λ ˆμ΄ν„°μ΄λ‹€. λ”°λ‘œ Symbol.iterator 호좜 μž‘μ—… 없이 μ΄ν„°λ ˆμ΄ν„°λ‘œ μΉ˜λΆ€λ˜λ‹ˆκΉŒ Symbol.iterator λ©”μ†Œλ“œλ‘œ μ΄ν„°λ ˆμ΄ν„°λ₯Ό 별도 생성할 ν•„μš”κ°€ μ—†λ‹€

● μ œλ„ˆλ ˆμ΄ν„°λ ˆμ„œ yieldλ₯Ό ν†΅ν•˜μ—¬ λͺ‡ 번의 next λ₯Ό 톡해 값을 κΊΌλ‚Ό 수 μžˆλŠ”μ§€ μ •ν•  수 μžˆλ‹€.

● next()ν•¨μˆ˜κ°€ μ‹€ν–‰λ˜λ©΄ yield μˆœμ„œλŒ€λ‘œ μ‹€ν–‰λ˜κ³  μΌμ‹œ μ€‘λ‹¨λœλ‹€.

  - start -> generatorObj.next() -> yield 1 -> generatorObj.next() -> yield 2 -> ... -> end

● μ œλ„ˆλ ˆμ΄ν„°μ˜ μ‹€ν–‰ κ²°κ³Όκ°€ μ΄ν„°λ ˆμ΄μ΄ν„°μ΄κΈ° λ•Œλ¬Έμ— for..of μ—­μ‹œ μ‚¬μš© κ°€λŠ₯ν•˜λ‹€.

  - 단, next()λ₯Ό 톡해 μˆœνšŒκ°€ λλ‚˜λ©΄ for..ofμ—μ„œ μ•ˆμ°νžŒλ‹€.

  - 그리고 nextμ™€λŠ” 달리 for..ofμ—μ„œ ν˜ΈμΆœμ—μ„œλŠ” return의 값은 μ°νžˆμ§€ μ•ŠλŠ”λ‹€.

● return을 ν•˜λ©΄ 리턴값을 value와 ν•¨κ»˜ done이 trueκ°€ λ˜λ©΄μ„œ 순회λ₯Ό μ’…λ£Œ ν•œλ‹€.

​

​

yield* (μ œλ„ˆλ ˆμ΄ν„° μ»΄ν¬μ§€μ…˜)

β€‹λ‹€μŒκ³Ό 같은 μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜λ € ν•œλ‹€.

μ²˜μŒμ—” 숫자 0λΆ€ν„° 9κΉŒμ§€λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€(문자 μ½”λ“œ 48λΆ€ν„° 57κΉŒμ§€),
μ΄μ–΄μ„œ μ•ŒνŒŒλ²³ λŒ€λ¬Έμž AλΆ€ν„° ZκΉŒμ§€λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€(문자 μ½”λ“œ 65λΆ€ν„° 90κΉŒμ§€).
μ΄μ–΄μ„œ μ•ŒνŒŒλ²³ μ†Œλ¬Έμž aλΆ€ν„° zκΉŒμ§€λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€(문자 μ½”λ“œ 97λΆ€ν„° 122κΉŒμ§€).

 

일반적인 for문을 μ‚¬μš©ν•œ κ΅¬ν˜„λ°©λ²•μ΄λ‹€.

function* generateAlphaNum() {

  for (let i = 48; i <= 57; i++) 
      yield i; // 0123456789

  for (let i = 65; i <= 90; i++) 
      yield i; // ABCDEFGHIJKLMNOPQRSTUVWXYZ

  for (let i = 97; i <= 122; i++) 
      yield i; // abcdefghijklmnopqrstuvwxyz
}

let str = '';
for(let code of generateAlphaNum()) {
  str += String.fromCharCode(code);
}

alert(str); // 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

​

ν•˜μ§€λ§Œ μ œλ„ˆλ ˆμ΄ν„°μ˜ 특수 문법 yield*λ₯Ό μ‚¬μš©ν•˜λ©΄ μ œλ„ˆλ ˆμ΄ν„°λ₯Ό λ‹€λ₯Έ μ œλ„ˆλ ˆμ΄ν„°μ— ‘λΌμ›Œ 넣을 수’ μžˆλ‹€.

yield에 *λ₯Ό λΆ™μ—¬ μ‚¬μš©ν•˜κ²Œ 되면, yield*와 ν•¨κ»˜ ν‘œν˜„λœ μ΄ν„°λŸ¬λΈ” 객체λ₯Ό μˆœνšŒν•˜κ²Œ λœλ‹€.

function* generateSequence(start, end) { // μ‹œμž‘κ³Ό 끝을 μ •ν•΄μ„œ μˆœνšŒν•˜λŠ” μ œλ„ˆλ ˆμ΄ν„°
  for (let i = start; i <= end; i++) 
     yield i;
}

function* generatePasswordCodes() {
/* μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό 싀행할땐 보톡 let a = generateSequence(48, 57);
   λ³€μˆ˜μ—λ‹€ 널고, a.next()λ₯Ό 톡해 μˆœνšŒν•œλ‹€.
   ν•˜μ§€λ§Œ yield* 에 λ°”λ‘œ μˆœνšŒκ°€ κ°€λŠ₯ν•˜λ‹€. 
   μ΄λŠ” 마치 비동기 νŒŒνŠΈμ—μ„œ Promise().then()보닀 await Promise() μ“°λŠ” 격과 λΉ„μŠ·ν•˜λ‹€κ³  보면 λœλ‹€.
*/

  // 0..9
  yield* generateSequence(48, 57); // generateSequence()ν•¨μˆ˜μ˜ 리턴값은 μ œλ„ˆλ ˆμ΄ν„° 객체이닀. yield*λŠ” μ œλ„ˆλ ˆμ΄ν„° 객체λ₯Ό μ­‰ μˆœνšŒμ‹œν‚¨λ‹€.

  // A..Z
  yield* generateSequence(65, 90);

  // a..z
  yield* generateSequence(97, 122);
}

let str = '';
for(let code of generatePasswordCodes()) {
  str += String.fromCharCode(code);
}

alert(str); // 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

​

+ μΆ”κ°€)

function* innerGenerator() {
  yield* ['a', 'b', 'c'];
}

function* generator() {
  yield [1, 2, 3]; // κ·Έλƒ₯ yield ν•˜λ©΄ λ°°μ—΄ 자체λ₯Ό μ€€λ‹€.

  yield* [4, 5, 6]; // yield*λŠ” 받은 값이 μ΄ν„°λ ˆμ΄ν„° 객체일경우 μˆœνšŒν•œλ‹€. 즉, 배열을 ν’€μ–΄μ„œ μˆœνšŒν•œλ‹€.

  yield* innerGenerator();
}

[...generator()];
// [[1, 2, 3], 4, 5, 6, 'a', 'b', 'c']
function* iterableYield() {
  const a = 1;
  yield a;
  yield* [1, 2, 3].map(el => el * (10 ** a)); // 결과인 배열은 μžμ²΄κ°€ μ΄ν„°λŸ¬λΈ” 객체이닀.

  const b = 2;
  yield b;
  yield* [1, 2, 3].map(el => el * (10 ** b));

  const c = 3;
  yield c;
  yield* [1, 2, 3].map(el => el * (10 ** c));
}

function run(gen) {
  const it = gen();

  (function iterate({ value, done }) {
    console.log({ value, done });
    if (done) {
      return value;
    }

    iterate(it.next(value)); // μž¬κ·€λ‘œ next()λ₯Ό 반볡
  })(it.next());
}

run(iterableYield);
// { value: 1, done: false }
// { value: 10, done: false }
// { value: 20, done: false }
// { value: 30, done: false }
// { value: 2, done: false }
// { value: 100, done: false }
// { value: 200, done: false }
// { value: 300, done: false }
// { value: 3, done: false }
// { value: 1000, done: false }
// { value: 2000, done: false }
// { value: 3000, done: false }
// { value: undefined, done: true }

 

 

next(μΈμžκ°’) μ „λ‹¬ν•˜κΈ°

μ•„λž˜ μ˜ˆμ‹œμ—μ„œλŠ” nextκ°€ μΈμžκ°’κ³Ό ν•¨κ»˜ 호좜 λ˜μ—ˆλ‹€.

첫 번째 호좜이 아무것도 좜λ ₯ν•˜μ§€ μ•Šμ€ 것은 Generatorκ°€ 아직 μ•„λ¬΄λŸ° 값도 yield ν•˜μ§€μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

(두 번째 호좜과 ν•¨κ»˜ μ „λ‹¬λœ μ •μˆ˜ 2λŠ” Generator λ‚΄λΆ€μ˜ yield ν‚€μ›Œλ“œμ— μ „λ‹¬λ˜μ–΄ value둜 ν• λ‹Ήλ˜μ—ˆκ³  console.log둜 좜λ ₯λ˜μ—ˆλ‹€)

function* gen() {
  while(true) {
    var value = yield null; // null값을 보내고, next(μΈμžκ°’)을 톡해 값을 λ°›λŠ”λ‹€.
    console.log(value);
  }
}

var g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// "{ value: null, done: false }"
// 2

 

 

... μ „κ°œμ—°μ‚°μž μ œλ„ˆλ ˆμ΄ν„°

spread문법은 μ΄ν„°λŸ¬λΈ” 객체에 ν•œν•΄μ„œ μž‘λ™ν•œλ‹€.

μ œλ„ˆλ ˆμ΄ν„°λŠ” μ΄ν„°λŸ¬λΈ”μ— μ΄ν„°λ ˆμ΄ν„°λΌμ„œ 이 μ—­μ‹œ 적용이 κ°€λŠ₯ν•˜λ‹€.

μ „κ°œμ—°μ‚°μžλ₯Ό μ΄μš©ν•˜λ©΄ ꡳ이 λ³€μˆ˜μ— λ„£κ³ , next()λ₯Ό 반볡문 ν•  ν•„μš”μ—†μ΄ λ°”λ‘œλ°”λ‘œ νŽΌμ³μ„œ μš”μ†Œκ°’λ“€μ΄ 순회 λ‚˜μ—΄ λœλ‹€.

function* generateName() {
  yield 'W';
  yield 'O';
  yield 'N';
  yield 'I';
  yield 'S';
  yield 'M';
}

// for..of
const genForForOf = generateName();
for (let i of genForForOf) {
  console.log(i);
}
// 'W'
// 'O'
// 'N'
// 'I'
// 'S'
// 'M'

// ...Spread
const genForSpread = generateName();
console.log([...genForSpread]); // ['W', 'O', 'N', 'I', 'S', 'M']

 

 

μ œλ„ˆλ ˆμ΄ν„° μ’…λ£Œ

μ œλ„ˆλ ˆμ΄ν„°μ—λŠ” next 외에도 throw, return λ“±μ˜ λ©”μ†Œλ“œκ°€ μžˆλŠ”λ°, 이 returnκ³Ό throwλ₯Ό 톡해 μ œλ„ˆλ ˆμ΄ν„°λ₯Ό μ’…λ£Œν•  수 μžˆλ‹€. λ‹€λ§Œ, 이 λ‘˜μ€ 쑰금의 차이가 μžˆλ‹€.

​

[return]

function* increment() {
  let i = 0;

  try {
    while (true) {
      yield i++;
    }
  } catch (e) {
    console.log('[ERROR]', e);
  }
}

const withReturn = increment();

console.log(withReturn.next()); // { value: 0, done: false } : i++ λΌμ„œ 0λΆ€ν„° μ°νžŒλ‹€.
console.log(withReturn.next()); // { value: 1, done: false }
console.log(withReturn.next()); // { value: 2, done: false }
console.log(withReturn.next()); // { value: 3, done: false }
console.log(withReturn.return(42)); // { value: 42, done: true }

 

return이 호좜되고 λ‚˜λ©΄, valueμ—λŠ” return의 μΈμžκ°€ ν• λ‹Ήλ˜κ³ , done은 trueκ°€ λœλ‹€.

​

​

[throw]

const withThrow = increment();

console.log(withThrow.next());
console.log(withThrow.next());
console.log(withThrow.next());
console.log(withThrow.next()); // { value: 3, done: false }
console.log(withThrow.throw(-1)); // Uncaught -1

throwκ°€ 호좜되고 λ‚˜λ©΄, catch 블둝에 throw의 μΈμžκ°€ μ „λ‹¬λœλ‹€.


μ‹€μ „) 지연 ν‰κ°€μ˜ μž₯점

1λΆ€ν„° 99κΉŒμ§€ μˆœνšŒν•  수 μžˆλŠ” 반볡자λ₯Ό λ§Œλ“€μ–΄ 보자.

​

λ¨Όμ € 배열을 λ§Œλ“œλŠ” 방법은,

function newArr(n) {
  let i = 1;
  const res = [];
  while (i < n) 
     res.push(i++);
  return res;
}
const arr = newArr(100);
console.log(arr); // [1,2,3 ... 99, 100]

​

μ œλ„ˆλ ˆμ΄ν„°λ‘œ λ§Œλ“œλŠ” 방법은,

function* newArrGen(n) {
  let i = 1;
  while (i < n) 
     yield i++;
}
const iter = newArrGen(100);
console.log(iter);

​

이제 μœ„μ˜ 두 ν•¨μˆ˜λ₯Ό 가지고 λ§Œλ“€μ–΄μ§„ 반볡 κ°€λŠ₯ν•œ 객체둜 5의 배수λ₯Ό μž‘μ€ μˆ˜λΆ€ν„° 2개만 찾도둝 κ΅¬ν˜„ν•΄λ³΄μž

function fiveArr(iter) {
  const res = [];
  for (const item of iter) {
    if (item % 5 == 0) 
       res.push(item);
    else if (res.length == 2) 
       break;
  }
  return res;
}

console.log(fiveArr(newArr(100)));
console.log(fiveArr(newArrGen(100)));

/* 
μ‹€ν–‰ κ²°κ³Ό
[ 5, 10 ] 
[ 5, 10 ]
*/

같은 κ²°κ³Όλ₯Ό λ§Œλ“€μ–΄λ‚Έλ‹€.

배열을 λ§Œλ“€μ–΄ λ„£μ–΄ λŒλ¦¬λ“ , μ œλ„ˆλ ˆμ΄ν„°λ‘œ λŒλ¦¬λ“  λ˜‘κ°™μ€ 순회λ₯Ό ν•˜λŠ” 것이닀.

​

ν•˜μ§€λ§Œ μ œλ„ˆλ ˆμ΄ν„°λ₯Ό ν™œμš©ν•œ μ½”λ“œλŠ” μ’€ 더 λΉ λ₯΄κ²Œ λ™μž‘ν•œλ‹€.

이 차이가 μ¦‰μ‹œ 평가와 지연 평가인데,

 

fiveArr(newArr(100))

μ½”λ“œλŠ” newArr ν•¨μˆ˜κ°€ 배열을 μ¦‰μ‹œ λ§Œλ“€μ–΄λ‚Έλ‹€. (1~99κΉŒμ§€) λ§Œλ“€μ–΄μ§„ 배열을 λ¦¬ν„΄ν•΄μ„œ fiveArr ν•¨μˆ˜λ₯Ό μˆ˜ν–‰ν•˜κ²Œ λœλ‹€.

μ‰½κ²Œ 말해 fiveArr([1, 2, 3, 4, 5, 6, ..., 97, 98, 99]) λœλ‹€λŠ” 것이닀.

​

fiveArr(newArrGen(100))

μ½”λ“œλŠ” newArrGen ν•¨μˆ˜κ°€ μ΄ν„°λ ˆμ΄ν„°λ§Œ λ§Œλ“€μ–΄λ‚΄κ³  fiveArr ν•¨μˆ˜μ—μ„œ ν•„μš”ν•  λ•Œ μ΄ν„°λ ˆμ΄ν„°μ—μ„œ ν‰κ°€λœ 값을 μ‚¬μš©ν•˜κ²Œ λœλ‹€.

 

κ·Ήλ‹¨μ μœΌλ‘œ 크기λ₯Ό μ˜¬λ €μ„œ μ‹œκ°„μ„ λΉ„κ΅ν•΄λ³΄μž.

console.time('');
console.log(fiveArr(newArr(10000000))); // [ 5, 10 ]
console.timeEnd(''); // : 285.535ms


console.time('');
console.log(fiveArr(newArrGen(10000000))); // [ 5, 10 ]
console.timeEnd(''); // : 7.296ms

μ–΄λ§ˆμ–΄λ§ˆν•œ 크기의 반볡 κ°€λŠ₯ν•œ 객체λ₯Ό λ§Œλ“  λ‹€μŒμ— fiveArr ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜λŠ” λͺ¨μŠ΅μ΄λ‹€.

κ·ΈλŸ¬λ‚˜ μ¦‰μ‹œ 평가와 달리 지연 ν‰κ°€λŠ” ν™•μ‹€νžˆ λΉ λ₯΄κ²Œ λ™μž‘ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

 

λ˜ν•œ, 값이 ν•„μš”ν•  λ•Œ μ΄ν„°λ ˆμ΄ν„°μ—μ„œ κΊΌλ‚΄ μ“°λ―€λ‘œ λ¬΄ν•œλŒ€λ‘œ μ΄ν„°λ ˆμ΄ν„°λ₯Ό λ§Œλ“€μ–΄λ„ κ²°κ³ΌλŠ” κ°™λ‹€.

console.time('');
console.log(fiveArr(newArrGen(Infinity))); // [ 5, 10 ]
console.timeEnd(''); // : 7.441ms

μ œλ„ˆλ ˆμ΄ν„° 비동기 처리

const fetch = require('node-fetch');

function getUser(genObj, username) {
  fetch(`https://api.github.com/users/${username}`)
    .then(res => res.json())
    // β‘  μ œλ„ˆλ ˆμ΄ν„° 객체에 비동기 처리 κ²°κ³Όλ₯Ό μ „λ‹¬ν•œλ‹€.
    .then(user => genObj.next(user.name));
}

// μ œλ„ˆλ ˆμ΄ν„° 객체 ν•œλ°© 생성
const g = (function* () {
  let user;
  // β‘‘ 비동기 처리 ν•¨μˆ˜κ°€ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•œλ‹€.
  // 비동기 처리의 μˆœμ„œκ°€ 보μž₯λœλ‹€.
  user = yield getUser(g, 'jeresig'); // genObj.next(user.name)에 μ˜ν•΄μ„œ μœ μ €μ΄λ¦„μ΄ λ°˜ν™˜λœλ‹€.
  console.log(user); // John Resig

  user = yield getUser(g, 'ahejlsberg');
  console.log(user); // Anders Hejlsberg

  user = yield getUser(g, 'ungmo2');
  console.log(user); // Ungmo Lee
}());

// μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ μ‹œμž‘
g.next();
  1. ꡉμž₯히 λ³΅μž‘ν•΄ 보이지면 별거없닀. μ°¨λ‘€λŒ€λ‘œ ν•΄λ³΄μž.
  2. μš°μ„  λ³€μˆ˜ g에 μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ μ¦‰μ‹œμ‹€ν–‰μœΌλ‘œ, λ°˜ν™˜κ°’ μ œλ„ˆλ ˆμ΄ν„° 객체λ₯Ό λ°›μ•˜λ‹€. 즉 λ³€μˆ˜ gλŠ” μ œλ„ˆλ ˆμ΄ν„° 객체.
  3. g.next()λ₯Ό 톡해 순회λ₯Ό μ‹œμž‘ν•œλ‹€. 그러면 첫 yield둜 κ°„λ‹€
  4. 이 첫 yieldμ—μ„œ getUser()ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•œλ‹€. 인자둜 call by reference둜 g객체λ₯Ό μ „λ‹¬ν•œλ‹€.
  5. getUser()ν•¨μˆ˜μ—μ„œ fetch된 결과둜 genObj.next(μΈμžκ°’)이 μ‹€ν–‰λœλ‹€. next에 값이 μžˆμœΌλ‹ˆ, λ°˜ν™˜κ°’μœΌλ‘œ λ³€μˆ˜ user에 μΈμžκ°’μ΄ λ“€μ–΄κ°€κ²Œ λœλ‹€.
  6. 이λ₯Ό λ°˜λ³΅ν•œλ‹€.

κ·Έλƒ₯ async / await μ“°λ©΄ κ°„λ‹¨ν•˜κ²Œ κ΅¬ν˜„ν• μˆ˜μžˆλ‹€.


*μš”μ•½*

β€‹μ œλ„ˆλ ˆμ΄ν„° 돌리기 μœ„ν•œ 3가지 방법.

  1. μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜ λ°˜ν™˜κ°’([Symbol.iterator]())을 λ°›μ•„μ„œ ν•˜λ‚˜μ”© next() ν•˜λŠ”λ²•
  2. μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό for..of문으둜 λŒλ¦¬λŠ” 방법
  3. μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜λ₯Ό [...] spreadλ¬Έλ²•μ¨μ„œ νŽΌμΉ˜λŠ” 법

Reference

https://mygumi.tistory.com/370

https://poiemaweb.com/es6-generator

https://wonism.github.io/javascript-generator/

https://bbaktaeho-95.tistory.com/80

​