资讯专栏INFORMATION COLUMN

ES6&ES7中的异步之Generator函数与异步编程

venmos / 2135人阅读

摘要:传统的异步方法回调函数事件监听发布订阅之前写过一篇关于的文章,里边写过关于异步的一些概念。内部函数就是的回调函数,函数首先把函数的指针指向函数的下一步方法,如果没有,就把函数传给函数属性,否则直接退出。

Generator函数与异步编程

因为js是单线程语言,所以需要异步编程的存在,要不效率太低会卡死。

传统的异步方法

回调函数

事件监听

发布/订阅

Promise

之前写过一篇关于Promise的文章,里边写过关于异步的一些概念。这篇文章将会说一下Generator函数的异步应用。

Generator函数 协程

多个线程互相合作完成任务,在传统的编程语言中(比如java),当A线程在执行,执行一段时间之后暂停,由B线程继续执行,B线程执行结束之后A线程再执行,这个时候,A线程就被称为协程,而这个协程A就是异步任务。

function* foo(){
    ... //其他代码
    var f = readFile();
    ... //其他代码
}

上边这个函数,foo函数就是一个协程,通过yield命令实现协程的暂停,等到读取文件的函数执行完毕之后,再继续执行foo其他的操作。

协程的Generator函数实现

Generator函数是协程在ES6的实现,最大的特点是交出函数的执行权(暂停函数执行)

整个Generator函数就是一个封装好了的异步任务,而yield是函数暂停执行的标志。

function* foo(){
    let x = 1;
    let y = yield x + 2;
    return y
}

var f = foo()
f.next();   // {value:3,done:false}
f.next();   // {value:undefined,done:true}

next方法的作用是分批端执行Generator函数。
异步函数的封装
let fetch = require("node-fetch")

function* asynsFun(){
    let url = "....";
    var f = yield fetch(url);
    console.log(f)
}

当执行完fetch之后把取回的数据赋值给f,然后再把f打印出来,这个看起来很像同步的写法,但是实现起来却是异步的。

是不是很简单,如果用回掉函数或者Promise的写法会很复杂的。

let a = asyncFun()
a.next()

a.value.then(function(data){
  return data.json();
}).then(function(data){
  g.next(data);
});

这样的写法表示起来很简洁,但是流程管理比较复杂。

Thunk函数

thunk函数是自动执行Generator函数的一种方法。

Thunk函数的核心理解就是传名调用。

function f(x){
    return x * 2
}

f(x + 6)

//等同于

var thunk = function(x) {
    return x + 5
}

function f() {
    return thunk() * 2 
}

理论上,x+6被thunk函数替代了,所有用到原来参数的地方,直接用thunk求值就行。

这就是Thunk函数的策略,是传名求值的一种实现策略,用来替换某个表达式。

js中的Thunk函数

在js中,函数的参数并不是传名的而是传值的,所以,thunk函数不是用来替换表达式的,而是用来替换多参数函数的。将其中一个参数替换成只接收一个回掉函数的单参数参数。

听起来很拗口,看代码。

// 正常的写法
fs.readFile(filename,callback);

// thunk函数的单参数版本
var thunk = function(filename) {
    return function(callback) {
        return fs.readFile(filename,callback);
    }
}

var readThunk = thunk(filename)
readThunk(callback)

理论上,只要函数的一个参数是回调函数,就可以改写成Thunk函数。

Thunkify模块

一个转换器,把函数转成Thunk函数

安装
npm install thunkify

使用方法:
var thunkify = require("thunkify");
var fs = require("fs");

var read = thunkify(rs.readFile);
read("package-json")(function(err,str)
    // ...
)

thunkify接受一个回调方法、

Generator函数的流程管理

之前说过,Generator函数的流程管理比较复杂,那么Thunk函数有什么用呢,正确答案是,他可以帮助Generator函数实现自动的流程管理。

function* gen(){
    // ...
}
var g = gen();
var res = g.next();

while(!res.done){
    console.log(res.value)
    res.next();
}

理论上,上面的代码可以实现自动执行,但是,不能适合异步。用Thunk可以解决这个问题。

var thunkify = require("thunkify");
var fs = require("fs");
var readFileThunk = thunkify(fs.readFile)

var gen = function* (){
    var r1 = readFileThunk("filename1")
    console.log(r1);
    var r2 = readFileThunk("filename2")
    console.log(r2);
    
}
Thunk函数的自动流程管理

Thunk函数的真正意义在于可以自动执行Generator函数,看下边的例子。

function* g(){
    // ...
}

function run(fn){    //Thunk函数接收一个Generator函数
    var gen = fn();
    
    function next(err,data){
        var result = gen.next(data);
        if(result.done) return;
        return result.value(next)
    }
    
    next();
}

run(g)

解析一下这个代码:
run方法其实就是一个Generator函数自动执行器。内部函数next就是Thunk的回调函数,next函数首先把Generator函数的指针指向Generator函数的下一步方法(gen.next()),如果没有,就把next函数传给Thunk函数(result.value属性),否则直接退出。

有了这个执行器,执行Generator函数就方便多了,不管内部多少操作,直接把Generator函数传给run函数即可,当然前提是每一个异步操作都是一个Thunk函数,也就是yield后面的必须是Thunk函数。

function* g(){
    var f1 = yield fs.readFileThunk("filename1")
    var f2 = yield fs.readFileThunk("filename2")
    ...

}

run(g)

Thunk 函数并不是 Generator 函数自动执行的唯一方案。因为自动执行的关键是,必须有一种机制,自动控制 Generator 函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,Promise 对象也可以做到这一点。

这篇文章写得比较难懂,其实主要是为了下一篇文章做铺垫。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89035.html

相关文章

  • ES6&ES7中的异步async函数

    摘要:更好的语义和分别表示异步和等待,比起和更容易理解。前边声明关键字,表示内部有内部操作,调用函数会返回一个对象。等价于其中函数就是自动执行器。 async函数 定义 async函数其实就是之前说过的Generator的语法糖,用于实现异步操作。它是ES2017的新标准。 读取两个文件: const fs = require(fs) const readFile = function(f...

    dongxiawu 评论0 收藏0
  • ES6&ES7中的异步Generator的语法

    摘要:第二次同理,遇到了第二个函数会停下来,输出的遍历器对象值为,的值依然是。比如返回的遍历器对象,都会有一个方法,这个方法挂在原型上。这三个函数共同的作用是让函数恢复执行。 Generator的语法 generator的英文意思是生成器 简介 关于Generator函数,我们可以理解成是一个状态机,里面封装了多种不同的状态。 function* gener(){ yield hel...

    djfml 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • ES6 Generator异步的同步书写

    摘要:返回值是一个对象,它的第一个属性是后面表达式的值或者的值第二个属性表示函数是否执行完成。真正的业务逻辑确实是用同步的方式写的。 开始前 我们从来没有停止过对javascript语言异步调用方式的改造,我们一直都想用像java那样同步的方式去写异步,尽管Promise可以让我们将异步回调添加到then方法中,但是这种调用方式仍然不那么优雅,es6 中新增加了generator,我们可以通...

    andycall 评论0 收藏0
  • Promise & Generator——幸福地用同步方法写异步JavaScript

    摘要:在这里看尤雨溪大神的这篇小短文,非常精简扼要地介绍了当前常用的。根据尤雨溪大神的说法,的也只是的语法糖而已。对象有三种状态,,。对象通过和方法来规定异步结束之后的操作正确处理函数错误处理函数。方便进行后续的成功处理或者错误处理。 最近在写一个自己的网站的时候(可以观摩一下~Colors),在无意识中用callback写了一段嵌套了5重回调函数的可怕的代码。回过神来的时候被自己吓了一跳,...

    Harpsichord1207 评论0 收藏0

发表评论

0条评论

venmos

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<