ES6 (7):Generator

Asynchronous programming scheme :( very practical drop)

Generator function is a normal function, but there are two features. First, functionthere is an asterisk between keywords and function names ; Second, the functions that use yieldan expression to define different internal states ( yieldmeaning in English is "output").

Every call nextmethod, internal pointer from one place to stop the function of the head or the execution, until the next yieldexpression (or returnstatement) so far. In other words, Generator function is performed segment, yieldthe expression is a marker suspended, and nextthe method can resume execution .

 

yieldGenerator expressions can only be used inside a function with an error in other places will.

var arr = [1, [[2, 3], 4], [5, 6]];

var flat = function* (a) {
  a.forEach(function (item) {
    if (typeof item !== 'number') {
      yield* flat(item);
    } else {
      yield item;
    }
  });
};

for (var f of flat(arr)){
  console.log(f);
}

 Because it is in the forEach (forEach itself is a function, it will be an error)

yieldIf you use an expression in another expression, it must be placed inside parentheses.

function* demo() {
  console.log('Hello' + yield); // SyntaxError
  console.log('Hello' + yield 123); // SyntaxError

  console.log('Hello' + (yield)); // OK
  console.log('Hello' + (yield 123)); // OK
}

 yieldExpressions as function parameters or on the right side of an assignment , you can not add brackets.

function* demo() {
  foo(yield 'a', yield 'b'); // OK
  let input = yield; // OK
}

 

The next method parameters: next method can be passed a parameter, this parameter will be used as the initial value of the execution time yield ( if the parameter is not passed, the default is undefined )

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

 

Mainly to see the first instance (b), the first call to next after the yield value of 6, when the next execution a second time, this time set yield is 12, which is above the red mark my place, (yield ( . 1 + X)) = 12 is, therefore y = 24, to the second line when the yield, as the case return yield 8, when the next call to the third method, the setting value is 13, the place marked red yield (y / 3) = 13, the z value 13, then to the return, the above results is performed at this time, x is 5, y is 24, z is 13, obtained by adding 42.

Note that if the yield is followed by an expression, then the initial value represents the value of the expression, that is, the yield followed. Examples of the above parameters did not pass because a call to the next method in the second time, the yield of the default value is undefined, so the value of NaN

 

for ... of: because the iterator Generator will generate run-time, so you can walk

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

 

Traverse the object:

* objectEntries function (obj) { 
  the let propKeys = Reflect.ownKeys (obj); // get the object of the Keys 

  for (the let propKey of propKeys) {// loop is executed, and returns the key value 
    the yield [propKey, obj [propKey]]; 
  } 
} 

the let {First = Jane: 'Jane', Last: 'Doe'}; 

for (the let [Key, value] of objectEntries (Jane)) { 
  the console.log (Key} {$ `:` $ {value}) ; 
}

 Because iterator object itself will generate a, it can be directly assigned to the object Symbol.iterator interface

function* objectEntries() {
  let propKeys = Object.keys(this);

  for (let propKey of propKeys) {
    yield [propKey, this[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

jane[Symbol.iterator] = objectEntries;

for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

 

x.throw: throw an exception (remember to use try catch capture)

function G * = var () { 
  the try { 
    the yield; 
  } the catch (E) { 
    the console.log ( 'inside of the trap', E); 
  } 
}; 

var I = G (); 
i.next (); 

the try { 
  I. the throw ( 'A'); 
  i.throw ( 'b'); 
} the catch (E) { 
  the console.log ( 'external capture', E); 
} 
// inside of the trap A 
// external capture b

 catch captures only once and return to capture yield a value of the latter

If the yield in try catch, then not return.

 

 2 does not return, because the yield in 2 try catch (and does not affect the traversal of the primary embodiment traversal continues after the error, the output 4).

 

 

Generator function error thrown vitro, in vivo function can be captured; in turn, the error thrown Generator function in vivo, in vitro function may also be catchcaptured.

 

 

Once the process Generator execution error is thrown and not caught inside, it will not perform anymore . If you then also invoke nextmethod will return a valueproperty equal to undefined, doneproperty equal to truethe object, the JavaScript engine has been running Generator think this over.

 

 

return:终结遍历Generator

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

 

yield* 表达式:在 Generator 函数内部,调用另一个 Generator 函数。

对比:

function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  }
  yield 'y';
}

for (let v of bar()){
  console.log(v);
}
// "x"
// "a"
// "b"
// "y"

 

 另外因为Generator 会生成一个遍历器对象,所以,会有下面这样的不同

function* inner() {
  yield 'hello!';
}

function* outer1() {
  yield 'open';
  yield inner();
  yield 'close';
}

var gen = outer1()
gen.next().value // "open"
gen.next().value // 返回一个遍历器对象
gen.next().value // "close"

function* outer2() {
  yield 'open'
  yield* inner()
  yield 'close'
}

var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"

 out1 在执行到第二行的时候返回的是一个遍历器对象。

 

任何数据结构只要有 Iterator 接口,就可以被yield*遍历

 

yield*后面的 Generator 函数(没有return语句时),等同于在 Generator 函数内部,部署一个for...of循环。(如果有return,那么return的值不会被for ...of 侦测到,但是可以作为想代理它的Generator 函数返回数据。)

function* concat(iter1, iter2) {
  yield* iter1;
  yield* iter2;
}

// 等同于

function* concat(iter1, iter2) {
  for (var value of iter1) {
    yield value;
  }
  for (var value of iter2) {
    yield value;
  }
}

 

function* foo() {
  yield 2;
  yield 3;
  return "foo";
}

function* bar() {
  yield 1;
  var v = yield* foo();
  console.log("v: " + v);
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}

 

对象属性:

let obj = {
  * myGeneratorMethod() {
    ···
  }
};
let obj = {
  myGeneratorMethod: function* () {  // ··· } };

 

协程:相比线程,由于 JavaScript 是单线程语言,只能保持一个调用栈。引入协程以后,每个任务可以保持自己的调用栈。这样做的最大好处,就是抛出错误的时候,可以找到原始的调用栈。不至于像异步操作的回调函数那样,一旦出错,原始的调用栈早就结束。

如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用yield表达式交换控制权。同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他协程都处于暂停状态。此外,普通的线程是抢先式的,到底哪个线程优先得到资源,必须由运行环境决定,但是协程是合作式的,执行权由协程自己分配

 (也就是说可以自己控制)。

 

异步操作同步化表达:Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()

// 卸载UI
loader.next()

 不用回调函数一个一个套了,看着多清晰~~

 

控制流:

一般如果异步操作,我们以前会这样做,一直套

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // Do something with value4
      });
    });
  });
});

 Promise写法:

Promise.resolve(step1)
  .then(step2)
  .then(step3)
  .then(step4)
  .then(function (value4) {
    // Do something with value4
  }, function (error) {
    // Handle any error from step1 through step4
  })
  .done();

 已经很清晰了。

 

function* longRunningTask(value1) {
  try {
    var value2 = yield step1(value1);
    var value3 = yield step2(value2);
    var value4 = yield step3(value3);
    var value5 = yield step4(value4);
    // Do something with value4
  } catch (e) {
    // Handle any error from step1 through step4
  }
}
scheduler(longRunningTask(initialValue));

function scheduler(task) {
  var taskObj = task.next(task.value);
  // 如果Generator函数未结束,就继续调用
  if (!taskObj.done) {
    task.value = taskObj.value
    scheduler(task);
  }
}

 

 

更加趋于函数式编程。

 

Guess you like

Origin www.cnblogs.com/jony-it/p/10972892.html