js异步请求发展史和yield

万恶的回调

对前端工程师来讲,异步回调是再熟悉不过了,浏览器中的各类交互逻辑都是经过事件回调实现的,前端逻辑愈来愈复杂,致使回调函数愈来愈多,同时 nodejs 的流行也让 javascript 在后端的复杂场景中获得应用,在 nodejs 代码中更是常常看到层层嵌套。javascript

如下是一个典型的异步场景:先经过异步请求获取页面数据,而后根据页面数据请求用户信息,最后根据用户信息请求用户的产品列表。过多的回调函数嵌套,使得程序难以维护,发展成万恶的回调php

$.get('/api/data', function(data) {
    console.log(data);
    $.get('/api/user', function(user) {
        console.log(user);
        $.get('/api/products', function(products) {
            console.log(products)
        });
    });
});

  

异步流程控制

  • 最原始异步流程的写法,就是相似上面例子里的回调函数嵌套法,用过的人都知道,那叫一个酸爽。html

  • 后来出现了 Promise ,它极大提升了代码的可维护性,消除了万恶的回调嵌套问题,而且如今已经成为 ES6 标准的一部分。前端

$.get('/api/data')
.then(function(data) {
    console.log(data);
    return $.get('/api/user');
})
.then(function(user) {
    console.log(user);
    return $.get('/api/products');
})
.then(function(products) {
    console.log(products);
});

  

  • 以后在 nodejs 圈出现了 co 模块,它基于 ES6 的 generator 和 yield ,让咱们能用同步的形式编写异步代码。
co(function *() {
    var data = yield $.get('/api/data');
    console.log(data);
    var user = yield $.get('/api/user');
    console.log(user);
    var products = yield $.get('/api/products');
    console.log(products);
});

  

  • 以上的 Promise 和 generator 最初创造它的本意都不是为了解决异步流程控制。其中 Promise 是一种编程思想,用于“当xx数据准备完毕,then执行xx动做”这样的场景,不仅是异步,同步代码也能够用 Promise。而 generator 在 ES6 中是迭代器生成器,被 TJ 创造性的拿来作异步流程控制了。真正的异步解决方案请你们期待 ES7 的 async 吧!本文如下主要介绍 co 模块。

co 模块

上文已经简单介绍了co 模块是能让咱们以同步的形式编写异步代码的 nodejs 模块,主要得益于 ES6 的 generator。nodejs >= 0.11 版本能够加 --harmony 参数来体验 ES6 的 generator 特性,iojs 则已经默认开启了 generator 的支持。java

要了解 co ,就不得不先简单了解下 ES6 的 generator 和 iterator。node

Iterator

Iterator 迭代器是一个对象,知道如何从一个集合一次取出一项,而跟踪它的当前序列所在的位置,它提供了一个next()方法返回序列中的下一个项目。算法

var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
var pair = it.next(); 
console.log(pair); // ["name", "JavaScript"]
pair = it.next(); 
console.log(pair); // ["birthYear", 1995]
pair = it.next(); // A StopIteration exception is thrown

  

乍一看好像没什么奇特的,不就是一步步的取对象中的 key 和 value 吗,for ..in也能作到,可是把它跟 generator 结合起来就大有用途了。编程

Generator

Generator 生成器容许你经过写一个能够保存本身状态的的简单函数来定义一个迭代算法。 Generator 是一种能够中止并在以后从新进入的函数。生成器的环境(绑定的变量)会在每次执行后被保存,下次进入时可继续使用。generator 字面上是“生成器”的意思,在 ES6 里是迭代器生成器,用于生成一个迭代器对象。后端

function *gen() {
    yield 'hello';
    yield 'world';
    return true;
}

  

以上代码定义了一个简单的 generator,看起来就像一个普通的函数,区别是function关键字后面有个*号,函数体内可使用yield语句进行流程控制。api

var iter = gen();
var a = iter.next();
console.log(a); // {value:'hello', done:false}
var b = iter.next();
console.log(b); // {value:'world', done:false}
var c = iter.next();
console.log(c); // {value:true, done:true}

  

当执行gen()的时候,并不执行 generator 函数体,而是返回一个迭代器。迭代器具备next()方法,每次调用 next() 方法,函数就执行到yield语句的地方。next() 方法返回一个对象,其中value属性表示 yield 关键词后面表达式的值,done 属性表示是否遍历结束。generator 生成器经过nextyield的配合实现流程控制,上面的代码执行了三次 next() ,generator 函数体才执行完毕。

co 模块思路

从上面的例子能够看出,generator 函数体能够停在 yield 语句处,直到下一次执行 next()。co 模块的思路就是利用 generator 的这个特性,将异步操做跟在 yield 后面,当异步操做完成并返回结果后,再触发下一次 next() 。固然,跟在 yield 后面的异步操做须要遵循必定的规范 thunks 和 promises。

yieldables

The yieldable objects currently supported are: - promises - thunks (functions) - array (parallel execution) - objects (parallel execution) - generators (delegation) - generator functions (delegation)

7行代码

再看看文章开头的7行代码:

function co(gen) {
    var it = gen();
    var ret = it.next();
    ret.value.then(function(res) {
        it.next(res);
    });
}

  

首先生成一个迭代器,而后执行一遍 next(),获得的 value 是一个 Promise 对象,Promise.then() 里面再执行 next()。固然这只是一个原理性的演示,不少错误处理和循环调用 next() 的逻辑都没有写出来。

下面作个简单对比: 传统方式,sayhello是一个异步函数,执行helloworld会先输出"world"再输出"hello"

function sayhello() {
    return Promise.resolve('hello').then(function(hello) {
        console.log(hello);
    });
}
function helloworld() {
    sayhello();
    console.log('world');
}
helloworld();

  

输出

> "world"
> "hello"

  

co 的方式,会先输出"hello"再输出"world"

function co(gen) {
    var it = gen();
    var ret = it.next();
    ret.value.then(function(res) {
        it.next(res);
    });
}
function sayhello() {
    return Promise.resolve('hello').then(function(hello) {
        console.log(hello);
    });
}
co(function *helloworld() {
    yield sayhello();
    console.log('world');
});

  

输出

> "hello"
> "world"

  

消除回调金字塔

假设sayhello/sayworld/saybye是三个异步函数,用真正的 co 模块就能够这么写:

var co = require('co');
co(function *() {
    yield sayhello();
    yield sayworld();
    yield saybye();
});

  

输出

> "hello"
> "world"
> "bye"

  

转载自:https://www.cnblogs.com/yelongsan/p/6369474.html

相关文章
相关标签/搜索