ES6中的异步编程:Generators函数(一)

接见原文地点

对ES6的generators的引见分为3个部份

  • 第一部份base引见及运用

  • 第二部份基于generators和Promise完成最壮大的异步处置惩罚逻辑

概述

Generator函数是协程在ES6的完成,用来做异步流程的封装,最大特性就是能够交出函数的实行权(即停息实行)。异常的奇葩,光看语法,几乎认不出这也是JavaScript了。由于能够运用yield语句来停息异步操纵,这让generators异步编程的代码,很像同步数据流要领一样。由于从语法角度来看,generators函数是一个状况机,封装了多个内部状况,经由过程iterator来分步挪用。

基础语法

2个关键字搞定generators语法

  • function与函数名直接的星号:*

  • 函数体内yield语句

function* testGenerator() {
    yield 'first yield';
    yield 'second yield';
    return 'last';
}

var gen = testGenerator();

console.log(gen.next().value);// first yield 
// { value: 'first yield', done: false }
console.log(gen.next().value);// second yield
// { value: 'second yield', done: false }
console.log(gen.next().value);// last 
// { value: 'last', done: true }

console.log(gen.next().value);// undefined

for…of遍历

for…of轮回能够自动遍历generators函数的iterator对象,且不再须要挪用next要领。for…of须要搜检iterator对象的done属性,假如为true,则完毕轮回,因而return语句不能被遍历到

for (let i of testGenerator) {
    console.log(i);
}
// first yield
// second yield

next要领的参数

yield句自身没有返回值,或许说老是返回undefined。next要领能够带一个参数,该参数就会被看成上一个yield语句的返回值。

function *gen(){
  let arr = [];
  while(true){
    arr.push(yield arr);
  }
}

var name = gen();

console.log(name.next('first').value);//[]
console.log(name.next('second').value);//["second"]
console.log(name.next('thrid').value);//["second","thrid"]

须要注重的是,第一次实行next设置参数没有结果。

generators实践

完成Fibonacci数列

递归完成:

function* fib (n, current = 0, next = 1) {
  if (n === 0) {
    return 0;
  }

  yield current;
  yield* fib(n - 1, next, current + next);
}

for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

注:假如存储计算结果再过运算,如许的完成比递归要领效率高3倍

function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (;;) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

应用for…of轮回,遍历恣意对象(object)的要领

原生的JavaScript对象没有遍历接口,没法运用for…of轮回,经由过程Generator函数为它加上这个接口,就可以够用了。

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

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

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

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

ES6中iterator遍历接口汇总

  • for…of轮回

  • 扩大运算符(…)

  • 解构赋值

  • Array.from要领内部挪用的

它们都能够将Generator函数返回的Iterator对象,作为参数来运用。

function* numbers () {
  yield 1
  yield 2
  return 3
}

// 扩大运算符
[...numbers()] // [1, 2]

// Array.from 要领
Array.from(numbers()) // [1, 2]

// 解构赋值
let [x, y] = numbers();
x // 1
y // 2

// for...of 轮回
for (let n of numbers()) {
  console.log(n)
}
// 1
// 2

generators与同步

generators一个特性就是代码看上去异常像同步编程的结果

function* test() {
    yield( "1st" );
    yield( "2nd" );
    yield( "3rd" );
    yield( "4th" );
}
var iterator = test();

console.log( "== Start of Line ==" );
console.log( iterator.next().value );
console.log( iterator.next().value );
for ( var line of iterator ) {
    console.log( line );
}
console.log( "== End of Line ==" );

看下输出,浓浓的同步实行作风。

== Start of Line ==
1st
2nd
3rd
4th
== End of Line ==

callback、Promises、Generators比较

举例说一个场景,查询一篇消息文章的作者信息,流程是:要求最新文章列表->要求某文章相干id->作者id信息

callback完成

getArticleList(function(articles){
    getArticle(articles[0].id, function(article){
        getAuthor(article.authorId, function(author){
            alert(author.email);
        })
    })
})

function getAuthor(id, callback){
    $.ajax(url,{
        author: id
    }).done(function(result){
        callback(result);
    })
}

function getArticle(id, callback){
    $.ajax(url,{
        id: id
    }).done(function(result){
        callback(result);
    })
}

function getArticleList(callback){
    $.ajax(url)
    .done(function(result){
        callback(result);
    });
}

用Promise来做

getArticleList()
.then(articles => getArticle(articles[0].id))
.then(article => getAuthor(article.authorId))
.then(author => {
    alert(author.email);
});

function getAuthor(id){
    return new Promise(function(resolve, reject){
        $.ajax({
            url: id+'author.json',
            success: function(data) {
              resolve(data);
          }
        })
    });
}

function getArticle(id){
    return new Promise(function(resolve, reject){
        $.ajax({
            url: id+'.json',
            success: function(data) {
              resolve(data);
          }
        })
    });
}

function getArticleList(){
    return new Promise(function(resolve, reject){
       $.ajax({
           url: 'all.json',
           success: function(data) {
             resolve(data);
         }
       }) 
    });
}

Gererator来完成

function* run(){
  var articles = yield getArticleList();
  var article = yield getArticle(articles[0].id);
  var author = yield getAuthor(article.authorId);
  alert(author.email);  
}

var gen = run();
gen.next().value.then(function(r1){
  gen.next(r1).value.then(function(r2){
      gen.next(r2).value.then(function(r3){
        gen.next(r3);
        console.log("done");
      })
  })
});

runGenerator的完成

每次都要手动去挪用next要领,照样会让代码变得冗杂,我们能够设想一个特地用来运转generators的要领,并能够笼统出来,今后就可以够做一个一致的error治理,或许猎取当地数据逻辑的变化。

Thunk函数要领

编译器的‘传名挪用’完成,将一切的参数放到一个暂时函数中,再将这个暂时函数作为参数传入到函数体中。该暂时函数就叫做Thunk函数。

任何函数,只需参数有回调函数,就可以写成Thunk函数的要领。下面就是简朴的Thunk函数转换器。

//es5
var Thunk = function(fn) {
    return function() {
        var args = Array.pototype.silce.call(argumnets);
        return function (callback) {
            args.push(callback);
            return fn.apply(this. args);
        }
    }
}

//es6
var Thunk = function(fn) {
    return function(...args) {
        return function(callback) {
            return fn.call(this, ...args, callback);
        }
    }
}

一个运用Thunk要领来完成readFile的例子

//一般版本的readFile(多参数)
fs.readFile(filename, callback);

//Thunk版本的readFile(单参数)
var readFileThunk = Thunk(filename);
readFileThunk(callback);

var Thunk = function(fileName) {
    return function(callback) {
        return fs.readFile(fileName, callback);
    }
}

能够看到,假如我们经由过程构建一个基于Thunk要领完成的runGenerators函数,能够很好的掌握我们的generators运转流程。

function *generator() {
    var articles = yield getArticleList();
    var article = yield getArticle(articles[0].id);
    var author = yield getAuthor(article.authorId);
    console.log(author.email);
}

function runGenerator() {
    var gen = generator();
    
    function go(result) {
        if(result.done) return;
        
        result.value.then(function(rsp) {
            go(gen.next(rsp));
        })
    }
    
    go(gen.next());
}

runGenerator();

参考

    原文作者:sutaking
    原文地址: https://segmentfault.com/a/1190000007504979
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞