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

抽象极昼
• 阅读 1645

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 对象也可以做到这一点。

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

点赞
收藏
评论区
推荐文章
晴空闲云 晴空闲云
3年前
JavaScript中MutationObServer监听DOM元素详解
DOM的MutationObServer接口,可以在DOM被修改时异步执行回调函数,我的理解就是可以监听DOM修改。基本使用可以通过MutationObserver构造函数实例化,参数是一个回调函数。jsletobservernewMutationObserver(()console.log("change"));console.log(obs
Chase620 Chase620
4年前
Promise从入门到拿Offer之手写Promise
1、Promise构造函数的实现Promise构造函数用来声明示例对象,需要传入一个执行器函数。其中包括resolve函数和reject函数,以及几个重要的属性:状态属性、结果属性和回调函数队列。构造函数的基本框架resolve函数用于异步处理成功后调用的函数。其中包括验证对象状态修改次数,修改promise实例对象状态,异步调用成功的回调函数
前端麦小子 前端麦小子
2年前
JavaScript异步的实现
你好,我是麦小子。编程领域的概念大多来自生活,异步也是如此。JS中的异步,有回调函数,期约,异步函数几种实现方式,一起探讨一下吧。
Stella981 Stella981
3年前
Generator函数
目录Generator语法yieldyield表达式next方法的参数Generator为什么是异步编程解决方案异步应用Thunk函数co模块Java
Wesley13 Wesley13
3年前
C# 1.0 新特性之异步委托(AP、APM)
Ø前言C异步委托也是属于异步编程中的一种,可以称为AsynchronousProgramming(异步编程)或者AsynchronousProgrammingModel(异步编程模型),因为这是实现异步编程的模式。委托是C1.0就有的特性,并且.NETv1.0同时也伴随有AsyncCallback、IAsyncResult
Stella981 Stella981
3年前
ES6中的Promise和Generator详解
简介ES6中除了上篇文章讲过的语法新特性和一些新的API之外,还有两个非常重要的新特性就是Promise和Generator,今天我们将会详细讲解一下这两个新特性。Promise什么是PromisePromise是异步编程的一种解决方案,比传统的解决方案“回调函数和事件”更合理和更强大。所谓P
Wesley13 Wesley13
3年前
JS异步的底层原理:单线程加事件队列
异步的底层原理:单线程事件队列。js的代码执行时单线程的,所谓单线程:就是js代码时从上到下按顺序依次执行的,一次只能做一件事情。事件队列可以看作一个容器,这个容器存储着js的回调函数,只有js代码执行结束后,才会去事件队列中调用这些回调函数。例:1//以下的代码先执行for循环,再输出sum值,然后输出正常代码执行,最后
Stella981 Stella981
3年前
JavaScript回调函数的高手指南
摘要:本文将会解释回调函数的概念,同时帮你区分两种回调:同步和异步。回调函数是每个前端程序员都应该知道的概念之一。回调可用于数组、计时器函数、promise、事件处理中。本文将会解释回调函数的概念,同时帮你区分两种回调:同步和异步。1.回调函数首先写一个向人打招呼的函数。只需要创建一个接受name参数的函数gree
Stella981 Stella981
3年前
ES6 Promise 对象扯谈
newPromise(/executor/function(resolve,reject){...});Promise的构造函数接收一个函数作为参数,函数里面传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来
Stella981 Stella981
3年前
ES6 Promise
Promisepromise是异步编程的一种解决方案1什么是异步?异步模式,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。"异步模式"非常重要。在浏
Stella981 Stella981
3年前
ES6——Generator
ES6新引入了Generator函数,可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。Generator函数组成Generator有两个区分于普通函数的部分一是在function后面,函数名之前有个\;函数内部有yield表达式。