拥抱Generator,告别异步回调

本节学习目标ajax

  • 实现一个生成器 :什么是迭代器?什么是生成器?generator概念,generator函数语法;编程

  • 异步处理 :了解异步编程;哪些解决办法;结合generator来解决异步;json


Generator概念

Generator很像是一个函数,可是你能够暂停它的执行。你能够向它请求一个值,因而它为你提供了一个值,可是余下的函数不会自动向下执行直到你再次向它请求一个值。api

什么是生成器?
function* quips(name) {
  yield "你好 " + name + "!";
  yield "你喜欢我吗";
  if (name.startsWith("yang")) {
    yield "咱们五百年前是一家诶";
  }
  yield "再见!";
}
> var iter = quips("yang jiang");
  [object Generator]
> iter.next()
  { value: "你好 yang jiang!", done: false }
> iter.next()
  { value: "咱们五百年前是一家诶", done: false }
> iter.next()
  { value: "再见!", done: false }
> iter.next()
  { value: undefined, done: true }

生成器调用看起来很是相似:quips("yang jiang")。可是,当你调用一个生成器时,它并不是当即执行,而是返回一个已暂停的生成器对象(上述实例代码中的iter)。你可将这个生成器对象视为一次函数调用,只不过当即冻结了,它刚好在生成器函数的最顶端的第一行代码以前冻结了。promise

每当你调用生成器对象的.next()方法时,函数调用将其自身解冻并一直运行到下一个yield表达式,再次暂停。浏览器

若是用专业术语描述,每当生成器执行yields语句,生成器的堆栈结构(本地变量、参数、临时值、生成器内部当前的执行位置)被移出堆栈。然而,生成器对象保留了对这个堆栈结构的引用(备份),因此稍后调用.next()能够从新激活堆栈结构而且继续执行。app

什么是迭代器?
class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; }

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

// 返回一个新的迭代器,能够从start到stop计数。
function range(start, stop) {
  return new RangeIterator(start, stop);
}

查看运行结果异步

生成器就是迭代器!全部的生成器都有内建.next()和[Symbol.iterator]()方法的实现。你只须编写循环部分的行为。async

function* range(start, stop) {
  for (var i = start; i < stop; i++)
    yield i;
}

咱们能够经过一个类比demo来进一步熟悉生成器,你可能对此感到陌生,可是并不难理解。异步编程

// 取货码1.0
function* ticketGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

var takeANumber = ticketGenerator()
console.log(takeANumber.next()) //{value: 1, done: false}
console.log(takeANumber.next()) //{value: 2, done: false}
console.log(takeANumber.next()) //{value: 3, done: false}
console.log(takeANumber.next()) //{value: undefined, done: true}
//取货码2.0
//无限循环取货码
function* ticketGenerator() {
  for(var i=0; true; i++) 
    yield i; //必定要打分号
}
var takeANumber = ticketGenerator()
console.log(takeANumber.next().value) //{value: 1, done: false}
console.log(takeANumber.next().value) //{value: 2, done: false}
console.log(takeANumber.next().value) //{value: 3, done: false}
console.log(takeANumber.next().value) //{value: 4, done: false}
//每一次当咱们调用next()时,
//generator执行下一个循环迭代而后暂停。
//这意味着咱们拥有一个能够无限向下运行的generator。
//由于这个generator只是发生了暂停,你并无冻结你的程序。
//事实上,generator是一个建立无限循环的好方法。
//取货码2.2
//经过改变给next传递一个值,它会被视为generator中的一个yield语句的结果来对待。
function* ticketGenerator(){
    for(var i=0; true; i++){
        var reset = yield i;
        if(reset) {i = -1;}
    }
}
var takeANumber = ticketGenerator(); 
console.log(takeANumber.next().value); //0  
console.log(takeANumber.next().value); //1 
console.log(takeANumber.next().value); //2 
console.log(takeANumber.next(true).value); //0 
console.log(takeANumber.next().value); //1

它与普通函数有不少共同点,可是两者有以下区别:

  • 普通函数使用function声明,而生成器函数使用function*声明。

  • 在生成器函数内部,有一种相似return的语法:关键字yield。两者的区别是,普通函数只能够return一次,而生成器函数能够yield屡次(固然也能够只yield一次)。在生成器的执行过程当中,遇到yield表达式当即暂停,后续可恢复执行状态。

这就是普通函数和生成器函数之间最大的区别,普通函数不能自暂停,生成器函数能够。

demo(斐波那契数列)

生成器函数和 yield 结合来生成斐波那契数列(前两个数字都是 1 ,除此以外任何数字都是前两个数之和的数列)

function fab(max) {
    var count = 0, last = 0, current = 1;

    while(count++ < max) {
        yield current;
        var tmp = current;
        current += last;
        last = tmp;
    }
}

for(var i of fib(10)) {
    console.log(i);
}

异步

Javascript语言的执行环境是"单线程"(single thread)。所谓"单线程",就是指一次只能完成一件任务。若是有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段Javascript代码长时间运行(好比死循环),致使整个页面卡在这个地方,其余任务没法执行。
为了解决这个问题,Javascript语言将任务的执行模式分红两种:同步(Synchronous)和异步(Asynchronous)。

  • 回调函数

  • 事件监听

  • 发布/订阅

  • Promises对象

f1().then(f2).fail(f3);

Promise

Promise有不少版本,也有不少实现的库,可是这里主要是介绍ES6标准的内容。若是阅读如下几条特性以为不懂的话建议先看看上面两本书相应的章节。

关于promise,首先要意识到它是一种对象。这种对象能够用Promise构造函数来建立,也能够经过Nodejs自己一些默认的返回来获取这种对象。
promise对象有三种状态:Pending,Fulfilled,Rejected。分别对应着未开始的状态,成功的状态,以及失败的状态。
这种对象经常封装着异步的方法。在异步方法里面,经过resolve和reject来划定何时算是成功,何时算是错误,同时传参数给这两个函数。这些参数就是异步获得的结果或者错误。
异步有成功的时候,也有错误的时候。对象经过then和catch方法来规定异步结束以后的操做(正确处理函数/错误处理函数)。而then和catch是Promise.prototype上的函数,所以“实例化”以后(其实并不是真正的实例)能够直接使用。
这个promise对象还有一个神奇的地方,就是能够级联。每个then里面返回一个promise对象,就又像上面所提的那样,有异步就等待异步,而后选择出规定好的正确处理函数仍是错误处理函数。

如何关停生成器

  • generator.return()

  • generator.next()的可选参数

  • generator.throw(error)

  • yield*

生成器能够用来实现异步编程,完成你用异步回调或promise链所作的一切。

生成器的.next()方法接受一个可选参数,参数稍后会做为yield表达式的返回值出如今生成器中。那就是说,yield语句与return语句不一样,它是一个只有当生成器恢复时才会有值的表达式。

结合生成器实现更多功能

普通yield表达式只生成一个值,而yield*表达式能够经过迭代器进行迭代生成全部的值。

function* concat(iter1, iter2) {
 for (var value of iter1) {
    yield value;
  }
  for (var value of iter2) {
    yield value;
  }
}
function* concat(iter1, iter2) {
      yield* iter1;
      yield* iter2;
}
fetch(url, {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    "Content-Type": "application/json"
  },
  credentials: "same-origin"
}).then(function(response) {
  response.status     //=> number 100–599
  response.statusText //=> String
  response.headers    //=> Headers
  response.url        //=> String

  return response.text()
}, function(error) {
  error.message //=> String
})

回调地狱

/**
* 第一個是抓取文章列表的api
* 第二個是給文章id, 抓取文章內容的api
* 第三個是給做者id, 返回做者資訊的api
*/

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

function getAuthor(id, callback){
    $.ajax("http://beta.json-generator.com/api/json/get/E105pDLh",{
        author: id
    }).done(function(result){
        callback(result);
    })
}

function getArticle(id, callback){
    $.ajax("http://beta.json-generator.com/api/json/get/EkI02vUn",{
        id: id
    }).done(function(result){
        callback(result);
    })
}

function getArticleList(callback){
    $.ajax(
    "http://beta.json-generator.com/api/json/get/Ey8JqwIh")
    .done(function(result){
        callback(result);
    });
}
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("http://beta.json-generator.com/api/json/get/E105pDLh",{
                author: id
            }).done(function(result){
                resolve(result);
            })
        });
    }

    function getArticle(id){
        return new Promise(function(resolve, reject){
            $.ajax("http://beta.json-generator.com/api/json/get/EkI02vUn",{
                id: id
            }).done(function(result){
                resolve(result);
            })
        });
    }

    function getArticleList(){
        return new Promise(function(resolve, reject){
           $.ajax(
            "http://beta.json-generator.com/api/json/get/Ey8JqwIh")
            .done(function(result){
                resolve(result);
            }); 
        });
    }
// 利用Generator的特性,來写出很像同步但实际上是非同步的代码
function* run(){
  var articles = yield getArticleList();
  var article = yield getArticle(articles[0].id);
  var author = yield getAuthor(article.authorId);
  console.log(author.email);  
}

var gen = run();
gen.next().value
    .then(articles => {
      gen.next(articles).value.then(article => {
        gen.next(article).value.then(author => {
          gen.next(author)
      })
    })
  })
async function run(){
  var articles = await getArticleList();
  var article = await getArticle(articles[0].id);
  var author = await getAuthor(article.authorId);
  alert(author.email);  
}
相关文章
相关标签/搜索