【一分钟JavaScript】Generator

267 阅读2分钟

什么是Generator

你可以理解为一个状态队列,遇到执行指令next时再调用yield参数。

function* gen() {
  console.log('step 1:');
  yield '1';
  console.log('step 2:');  
  yield '2';
  console.log('step 3:');
  return '3';
  console.log('step 4:');
}
var g=gen();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());

执行结果:

"step 1:"
Object {
  done: false,
  value: "1"
}
"step 2:"
Object {
  done: false,
  value: "2"
}
"step 3:"
Object {
  done: true,
  value: "3"
}
Object {
  done: true,
  value: undefined
}

可以看到gen的实例执行next()方法,每遇到yield就执行一次并且挂起,直到下一个next()。 直到最后一个yield执行完,下一次执行next()返回{done:true,value:3}。如果再次执行next()则返回{done:true,value:undefined}。

这样就实现了类似于异步线程的同步:想象一下,原来是这些异步函数各自独立执行,执行时间未知,现在是必须一条一条执行完。

yield 后面可以跟返回值,也可以是执行语句。

yield console.log(3+2);

注意

  • Generator函数使用的是自执行函数,而不是new实例化,否则会报错;
  • yield必须只能在Generator内使用,否则报错。

yield*

yield* 相当于执行另一个Generator的引用,类似于include。

// yield*
function* a(){
	yield 'a';
}
function* b(){
	yield 'b';
	yield* a();
	yield 'c';
}
var B=b();
console.log(B.next());
console.log(B.next());
console.log(B.next());
console.log(B.next());

结果:

{value: "b", done: false}
{value: "a", done: false}
{value: "c", done: false}
VM196:1 {value: undefined, done: true}

使用场景

  • 异步操作的同步化
function workflow(){
    showLoadingSplash();
    yield loadData();
    hideLoadingSplash();
}
var loader=workflow();
loader.next();//加载UI
load.next();//卸载UI
  • 控制流程

如果是一个多步操作,使用回调函数的写法如下:

step1(function(value1){
    step2(function(value2){
        step3(function(value3){
            //
        }
    }
}

Promise的写法:

Promise.resolve(step1)
    .then(step2)
    .then(step3)
    .then(function(value3){
        //resolved
    },function(error){
      //reject  
    })

Generator写法:

function *workflow(value1){
    try{
        var value2 = yield step1(value1);
        var value3 = yield step1(value2);
        var value4 = yield step1(value3);
    }catch(e){
        
    }
}
function scheduler(task){
    var taskObj=task.next(task.value);
    if(!taskObj.done){
        task.value=taskObj.value;
        scheduler(task);
    }
}
scheduler(workflow(initialValue));
  • 实例遍历Iterator接口
function* iterEntries(obj){
    let keys=Object.keys(obj);
    let key=null;
    for(let i=0,len=keys.length;i<len;i++){
        key=keys[i];
        yield [key, obj[key]];
    }
}

let entity={name:'Migao',id:'walkingp'};
for(const [key,value] of iterEntries(entity)){
  console.log(`${key}: ${value}`);
}

结果:

"name: Migao"
"id: walkingp"