JavaScript(ES 6):yieldの反復器とジェネレータ(Generator)


反復器(Iterator)とジェネレータ(Generator)
前言
反復器がjavaを学んだとき、mapを巡って反復器しか使えなかったことを最初に知った.具体的にどのように使うかは今あまり忘れていません.他の言語の大部分は反復器を持っているはずで、SQLもcursor( )を持っています.
存在は意味がある.
反復器のない日
ループ文がデータを反復する場合は、オブジェクトを初期化してこの値を記録する必要があります.
let array = [1,2,3];
for(let i = 0; i < array.length ; i++){
     
    var result = array[i];
}

上記のように、resultは、反復中の各データを記録する.
**利点:**構文が簡単;非利点:下付きスケールを自分で処理し、一度にデータをループし、ブロックしない必要があります.
これらの点もメリットとデメリットではないような気がしますが、なぜ反復器とジェネレータがあるのでしょうか.
一、反復器
1、反復器は対象
反復器の内部には反復専用インタフェースとメソッドがあり、すべての反復器にはnextメソッドがあり、オブジェクトを返します.
function next(){
     
    return {
     
        value:[any],//      
        done:[boolean]//      
    }
}

nextドロップダウンを呼び出すたびに、反復が完了するまで下に移動し、その後の反復は最後の値になります.
2、簡易反復器
var getIterator = function (array) {
     
  var index = 0;
  var length = array.length;
  return {
     
    next: function () {
     
      var done = index >= length;
      var value = done ? undefined : array[index++]
      return {
     
        done : done,
        value : value
      }
    }
  }
}

var iterator = getIterator(["A","B","C"]);
console.log(iterator.next());//{done: false, value: "A"}
console.log(iterator.next());//{done: false, value: "B"}
console.log(iterator.next());//{done: false, value: "C"}
console.log(iterator.next());//{done: true, value: undefined}
console.log(iterator.next());//{done: true, value: undefined}


以上は汎用型対配列反復であり,特定の構造の反復にのみ適している.反復オブジェクトを生成するために、ES 6はジェネレータを作成します.
二、ジェネレータ
1、ジェネレータとは
ジェネレータは反復器に戻る関数であり、functionの後に*を付けることによって表され、関数体内ではyieldを使用して次の反復データを表す.
const getIterator = function *() {
     
  yield "A";
  yield "B";
  yield "C";
}
const iterator = getIterator();
console.log(iterator.next());//{done: false, value: "A"}
console.log(iterator.next());//{done: false, value: "B"}
console.log(iterator.next());//{done: false, value: "C"}
console.log(iterator.next());//{done: true, value: undefined}
console.log(iterator.next());//{done: true, value: undefined}


2、ジェネレータの使い方
ジェネレータは本質的に反復器を返す関数です.
  • function *()はこの関数を表し、
  • yieldはreturnに相当し、呼び出すたびに“return”
  • の使用を終了する.
    return短絡のような特性を利用して、私たちはこのようにすることができます.
    const getIterator = function *() {
         
      console.log(" 1   next")
      yield "A";
      console.log(" 2   next")
      yield "B";
      console.log(" 3   next")
      yield "C";
    }
    console.log(iterator.next());// 1   next {done: false, value: "A"}
    console.log(iterator.next());// 2   next {done: false, value: "B"}
    console.log(iterator.next());// 3   next {done: false, value: "C"}
    console.log(iterator.next());//{done: true, value: undefined}          undefined          “yeild”    undefined
    
    1.2 の車輪を作ります.
    const getIterator = function *(array) {
         
        for(let i = 0 ;i < array.length; i++){
         
           yield array[i];
        }
    }
    

    3、注意事項
  • yieldは、ジェネレータ内部でのみ使用できる
  • である.
    const getIterator = function *(array) {
         
      array.forEach(function(item){
         
        yield item
      })
    }
    
    const getIterator = function *(array) {
         
      array.forEach(item => {
         
        yield item
      })
    }
    

    上記の例のコードはwebstormの静的検証さえ通過できません.ここでyieldはforeachのコールバック関数の内部にあるので、lambda式でもエラーが報告されます.
  • lambdaを使用してジェネレータを作成することはできません.たとえば、
  • const getIterator =  *(array) => {
         
       yield 1
    }
    
  • returnは反復を終了し、反復を早期に終了する
  • const getIterator = function *() {
         
      console.log(" 1   next")
      yield "A";
      console.log(" 2   next")
      return "B";
      console.log(" 3   next")
      yield "C";
    }
    iterator = getIterator()
    console.log(iterator.next());// 1   next {value: "A", done: false}
    console.log(iterator.next());// 2   next {value: "B", done: true}
    console.log(iterator.next());// 3   next {value: undefined, done: true}
    

    三、for-ofサイクル
    1、反復器の循環
    反復器はfor-ofで循環できます.
    const getIterator = function *() {
         
      yield "A";
      yield "B";
      yield "C";
    }
    const iterator = getIterator()
    for(let a of iterator){
         
      console.log(a)
    }
    // A B C
    
    

    2、反復可能なオブジェクトは、for-ofを使用してループできます.
    すべてのセット(ArraySetMap)および文字列は反復可能なオブジェクトです.
    const array = [1,2];
    for(let a of array){
         
        console.log(a);
    }
    // 1 2
    const str = "12";
    for(let a of str){
         
        console.log(a);
    }
    // 1 2
    

    これらのオブジェクトの内部には
    Array.protptype[Symbol.iterator] = *function(){
         
        for(let data in this){
         
            yeild data
        }
    }
    

    つまりこれらのオブジェクトは です
    類似のホイールを作成するには、次の手順に従います.
    const myArray = function(array){
         
      this[Symbol.iterator] = function *(){
         
        for(let data in array){
         
          yield data
        }
      }
    }
    
    var  iterator = new myArray([1,2,3,4])
    console.log(iterator)// myArray {Symbol(Symbol.iterator): ƒ}
    for(let a of iterator){
         
      console.log(a)
    }
    //1 2 3 4
    

    3、デフォルトの反復器
  • 1は、1得られるfor ofに従って反復器
  • を用いる.
  • 2 2得られる配列などの反復可能なオブジェクトにはデフォルトの反復器があるので、
  • const array = [1,2,3];
    let iterator = array[Symbol.iterator]();
    for(let a of iterator){
         
      console.log(a)
    }
    //1 2 3 
    iterator = array[Symbol.iterator]();
     console.log(iterator.next());
     // {value : 1, done :false}
    

    4、その他
    反復器はfor-ofで循環することができ、循環するたびに反復可能な内容かどうかをチェックします.検証の論理は次のようになります.
    const isIterator = Clazz => typeof Clazz.prototype[Symbol.iterator] === 'function';
    

    しかし、正確でない場合もあります.例えば、
    const myArray = function(array){
         
      this[Symbol.iterator] = function *(){
         //            ,       ,           
        for(let data in array){
         
          yield data
        }
      }
    }
    console.log(isIterator(myArray));//false
    

    したがって、次のようになります.
    const isIterator = Clazz => {
         
       return typeof  new Clazz()[Symbol.iterator] === 'function';
    }
    console.log(isIterator(myArray));//true
    

    四、高度な反復機能
    1、反復器パラメータ
  • nextメソッドパラメータの値は、前回yieldが返した値:
  • を置き換えます.
    let getIterator = function *() {
         
      console.log(" 1   next")
      let first = yield "A";
      console.log(" 2   next")
      let second = yield first + "B";
    }
    let iterator = getIterator();
    console.log(iterator.next());//{value: "A", done: false}
    console.log(iterator.next());//{value: "undefinedB", done: false}
    console.log(iterator.next());//{value: undefined, done: true}
    
     iterator = getIterator();
    console.log(iterator.next(1));//{value: "A", done: false}
    console.log(iterator.next(2));//{value: "2B", done: false}
    console.log(iterator.next(3));//{value: undefined, done: true}
    
    getIterator = function *() {
         
      console.log(" 1   next")
      let first = yield "A";
      console.log(" 2   next")
      let second = yield first + "B";
      yield second + "C"
    }
    iterator = getIterator();
    console.log(iterator.next(1));//{value: "A", done: false}
    console.log(iterator.next(2));//{value: "2B", done: false}
    console.log(iterator.next(3));//{value: "3C", done: true}
    //          :
    // iterator.next(1) =>  console.log(" 1   next"); yield "A"
    // iterator.next(2) =>  console.log(" 2   next");let first = 2; yield first + "B";
    // iterator.next(3) =>  console.log(" 3   next");let second  = 3; yield second + "C"
    
  • 上記のように、1回目のnextメソッドはどうしても何が入っても破棄されます
  • .
    2、異常放出
  • 注入エラー、強制的に反復を阻止する:
    const getIterator = function *() {
           
      yield "A";
      yield "B";
      yield "C";
    }
    iterator = getIterator();
    console.log(iterator.next());//{value: "A", done: false}
    console.log(iterator.throw(new Error("Boom Sakalaka")));//Uncaught Error: Boom Sakalaka
    //       
    console.log(iterator.next());
    //        iterator,    iterator.next() {value: undefined, done: true}
    // iterator.next() =>  yield "A"
    // iterator.throw(new Error("Boom Sakalaka")) => error,  yield "B"      
    
    
  • 反復取得
  •  const getIterator = function *() {
         
        let message ="";
        yield "A";
        try{
         
            yield "B";
        }catch(e){
         
            message = e.toString()
        }
        yield message+"C";
      }
      iterator = getIterator();
     console.log(iterator.next());//{value: "A", done: false}
     console.log(iterator.next());//{value: "B", done: false}
     console.log(iterator.throw(new Error("Boom Sakalaka")));//{value: "Error: Boom SakalakaC", done: false}
     console.log(iterator.next());//{value: undefined, done: true}
    

    以上の例から分かるように、
    throwも反復であり、通常のnextメソッドが戻り値yield valueを実行する前にエラーを投げ出し、エラーが処理されていない場合は反復を直接終了する.
    (throwの位置に注意):
     const getIterator = function *() {
         
        let message ="";
        yield "A";
        try{
         
            yield "B";
            throw new Error("Boom Sakalaka");
        }catch(e){
         
            message = e.toString()
        }
        yield message+"C";
      }
      iterator = getIterator();
     console.log(iterator.next());//{value: "A", done: false}
     console.log(iterator.next());//{value: "B", done: false}
     console.log(iterator.next());//{value: "Error: Boom SakalakaC", done: false}
     console.log(iterator.next());//{value: undefined, done: true}
    

    3、委託生成器
    複数のジェネレータを1つに統合する必要がある場合があります.構文:
    getIteratorFunc =  function*() {
         
        yield * 【     】
    } 
    
  • 簡単な依頼、例を挙げると
  • 幼稚園発果物:りんご3個、バナナ2本、かご2個.りんごを出し終わってから、バナナを出すことができます.この时、先生はかごを一つしか持っていません.りんごとバナナをかごに入れて、問題を解決します.
    getAppleIterator =  function*() {
         
        yield "  1"
        yield "  2"
        yield "  3"
    }
    getBananaIterator = function*(){
         
        yield "  1"
        yield "  2"
    }
     apple = getAppleIterator()
    console.log(apple.next());//{value: "  1", done: false}
    console.log(apple.next());//{value: "  2", done: false}
    console.log(apple.next());//{value: "  3", done: false}
    
    banana = getBananaIterator()
    console.log(banana.next());//{value: "  1", done: false}
    console.log(banana.next());//{value: "  2", done: false}
    
    

    次のように書くことができます.
    getFruitIterator =  function*(){
         
        yield *getAppleIterator();
        yield *getBananaIterator();
    }
    fruit = getFruitIterator();
    console.log(fruit.next());//{value: "  1", done: false}
    console.log(fruit.next());//{value: "  2", done: false}
    console.log(fruit.next());//{value: "  3", done: false}
    console.log(fruit.next());//{value: "  1", done: false}
    console.log(fruit.next());//{value: "  2", done: false}
    
  • ハイエンド委託:ジェネレータ戻り値による複雑なタスクの処理
  • getAppleIterator =  function*() {
         
        yield "  1"
        yield "  2"
        return 1
    }
    getBananaIterator = function*(count){
         
        yield `  ${
           count}`
    }
    getFruitIterator =  function*(){
         
       const count =  yield *getAppleIterator();
       yield *getBananaIterator(count);
    }
    fruit = getFruitIterator();
    console.log(fruit.next());//{value: "  1", done: false}
    console.log(fruit.next());//{value: "  2", done: false}
    console.log(fruit.next());//{value: "  1", done: false}
    console.log(fruit.next());//{value: undefined, done: true}
    
  • 反復文字列、配列などを依頼して反復できる:
  • getIterator =  function*() {
         
        yield * "123"
    } ;
     it =  getIterator();
    console.log(it.next());//{value: "1", done: false}
    console.log(it.next());//{value: "2", done: false}
    console.log(it.next());//{value: "3", done: false}
    console.log(it.next());//{value: undefined, done: true}
    getIterator =  function*() {
         
        yield * [1,2,3]
    } ;
     it =  getIterator();
    console.log(it.next());//{value: 1, done: false}
    console.log(it.next());//{value: 2, done: false}
    console.log(it.next());//{value: 3, done: false}
    console.log(it.next());//{value: undefined, done: true}
    

    *五、非同期タスク実行
    そんなにたくさん話したのに,まだ何の役にも立たない.これらの新しい特性が本当に存在する意味はおそらく非同期プログラミングである.ajaxリクエストの例を挙げます.
    $.ajax({
         
        url:"xxxxx.do",
        success:function(value){
         
            console.log(value)
        }
    })
    

    ネストが必要な場合は、次のようになります.
    $.ajax({
         
        url:"xxxxx.do",
        success:function(value){
         
           $.ajax({
         
                url:"xxxxx.do",
                success:function(value){
         
                  $.ajax({
         
                    url:"xxxxx.do",
                    success:function(value){
         
                        console.log(value)
                    }})
                }
            })
        }
    })
    

    以上の知識点に基づいて、
  • yieldは関数実行プロセスを一時停止し、次のnextの反復を待つ:
  • 反復器nextでパラメータが入力されると、次のnext実行操作によって取得される.

  • 一般的には、ビジネスでジェネレータを使用して、反復可能なデータを与えることを考えています.また、ジェネレータにビジネスを書き、反復ツール(タスク実行ツール)によって反復を支援することもできます.いつnextになるかは気にしない.
    1、簡単なタスク実行器
    requestIteratorCreator = function*(){
         
       const result = yield 2;
       yield result
    }
    run = function(requestIteratorCreator){
         
        task = requestIteratorCreator();
    	let result = task.next();
    	doTask = function(){
         
            if(!result.done){
         
                result = task.next(result.value);
                doTask()
    		}
    	}
    	doTask()
    }
    
    run(function*(){
         
        const result = yield 1;//
        console.log(result);//1
    })
    

    2、非同期処理を加える
    ajaxRequest = function(url,callback){
         
            setTimeout(function(){
         
                callback(`    ${
           url}   `)
            },1000);
        }
    //       :
    ajaxRequest("/user/login",function(data){
         
        console.log(data,"    !");
        console.log("          ")
        ajaxRequest("/user/getUserInfo",function(data){
         
        	console.log(data,"          !");
    	});
    });
    //    /user/login        !
    //          
    //    /user/getUserInfo              !
    

    何度もネストをお願いしたら、それは地獄のコールバックです.yieldを使用してこの特性を一時停止し、next伝達値を使用して非同期を処理することができます.
    requestCreater = function(url){
         
       return function(callback){
         
           return ajaxRequest(url,callback)
       } 
    }
    
    run = function(requestIteratorCreator){
         
         task = requestIteratorCreator();
    	 result = task.next();
         doTask = function(){
         
             if(!result.done){
         
                 if(typeof result.value === 'function'){
         
                 	result.value(function(data){
         
                           result = task.next(data);
                           doTask()
                 	})
                 }else{
         
                    result = task.next(task.value);
                    doTask()
                 }
             }
         }
         doTask()
    }
    
    run(function *(){
         
        //      
        const loginResult = yield requestCreater("/user/login")
        console.log(loginResult)
        const userInfoResult = yield requestCreater("/user/userInfo")
        console.log(userInfoResult)
    })
    //    /user/login   
    //    /user/userInfo   
    //    !!!!
    

    非同期でデータを処理し、ES 6はより興味深いPromiseを提供します.
    付録:ES 2018(ES 9)非同期反復
    上記の非同期処理で述べたPromiseは,ES 7で発揚し,その後ES 9に非同期反復法を加えた.まず、async-await ES 7のasync-awaitが私たちを光に連れて行ったことを理解してみましょう.
    fetchRequest = function(url){
         
      return new Promise((resolve,reject) =>{
         
        setTimeout(function(){
         
          resolve(`    ${
           url}   `)
        },1000);
      })
    }
    async function runFetchDemo() {
         
    	const loginInfo = await fetchRequest("/user/login");
    	console.log(loginInfo)
    	const userInfo = await fetchRequest("/user/userInfo");
    	console.log(userInfo)
    }
    runFetchDemo();
    //    /user/login   
    //    /user/userInfo   
    

    2、Promiseの非同期反復
    getFetchIterator = function*(){
         
        yield fetchRequest("/user/login");
        yield fetchRequest("/user/userInfo");
    }
    async function runFetchDemo() {
         
       asyncIterator = getFetchIterator();
      const loginInfo =  await asyncIterator.next();
      console.log(loginInfo)
      const userInfo =  await asyncIterator.next();
      console.log(userInfo)
      //        padding Promise,         
      
      asyncIterator = getFetchIterator();
      for await (const item of asyncIterator) {
         
        console.log(item)
      }
      //    /user/login   
      //    /user/userInfo   
    }
    

    ずいぶん簡略化されたと思いますか?はい、終わりました.