资讯专栏INFORMATION COLUMN

用co玩转异步

microelec / 2032人阅读

摘要:否则不会得到异步之后的值对象的值,并没有在中进行处理,而是直接作为返回值返回到对象外面了这就是的魔法。当生成器函数内的逻辑执行完毕且没有错误之后,这个对象返回值变为状态,且将生成器的返回值作为出来的值。

之前我在关于Promise的文章中提到了co这个库。在这篇文章里,我将写一写自己对它的认识。

Trust me,用了co库,你不想用别的,来它半斤异步调用你一口能吃仨。

但是我对Tj大神的co库源码谈不上深入理解。所以,如有乱讲,欢迎指正。

我这里默认读者对PromiseGenerator有一定的认识。

先安利自己写的两篇关于Promise的文章:

浅析ES6原生Promise

再谈Promise

下面我就来谈谈co这个牛逼的库。

ES7 async/await

干嘛,我们不是讲ES6么,怎么跳到ES7了?

因为co要做的事情,就是ES7的async/await要做的事情。
也就是说,这种解决异步的思路,已经在ECMA标准的考虑之中了。将来我们浏览器的JS引擎就可以原生实现这件事而不是通过JavaScript代码模拟。要知道,引擎的实现和代码的实现那是完全两码事。

一点题外话

多一句嘴:有些同学混淆了ECMA标准、引擎支持和代码实现的联系。

这里引用老赵在知乎里面回答问题时说的一句话:

ES7是个标准,定义的是what to do不是how to do,为什么好多人还是搞不清这两者的区别。

ECMAScript定义了一些JavaScript语言层面要做的事情,这是一个标准。之所以要制定这个标准,是为了防止浏览器各自为政而出现JS引擎对同一行代码的解释出现不同的情况。

也就是说,ECMA制定标准,我们就可以按照这个标准来写JavaScript代码。写好的JavaScript代码由浏览器的JS引擎来解释,最终变成计算机能读懂的代码来执行。

async/await

上代码:

var foo = function(){

    return new Promise(resolve => {
        // 异步操作之后
        resolve("OK");
    });
}

async funtion bar(){
    
    var result = await foo();
    
    console.log(result); 
    
}

bar(); // ==> 打印"OK"

我们注意到,这段代码用了两个新的关键字asyncawait。而且有两件神奇的事情发生了:

bar函数中包含了一个返回Promise对象的语句,而且Promise中存在异步代码。但是这条语句接下来的语句明显是等待Promise对象中的代码异步执行完毕之后才执行的。(否则不会得到异步之后的值)

Promise对象resolve的值,并没有在then中进行处理,而是直接作为返回值返回到Promise对象外面了.

这就是async/await的魔法。在函数前面加上async关键字之后,内部的代码会识别await关键字。此时假设await后面的语句返回一个Promise对象,那么执行的代码将会等待,直到Promise对象变为resolve状态。并且Promise对象中resolve的值将直接作为await语句的返回值返回。然后再执行await语句之后的语句。

从此我们就可以无痛的撸异步代码,妈妈再也不用担心回调金字塔的出现和异步流程逻辑搞不定的情况了!

另一个奇妙的事情就是,率先支持这一特性的浏览器居然是微软的Edge。大概是因为C#语言早就出现async/await,并且TypeScript也支持这一特性的缘故吧。

co

我们希望所有的浏览器都及早支持这一特性。但是值得欣喜的一点就是,虽然V8还没有支持,Tj大神早就利用Generator的方式实现了一个ES6版本的async/await!(膜拜脸)

co函数形式

同样是上面的逻辑,我们用co实现一次:

// 首先我们需要将co引入,假设我们使用commonJS的方式  

const co = require("co");

var foo = function(){

    return new Promise(resolve => {
        // 异步操作之后
        resolve("OK");
    });
}

co(function* (){
    
    var result = yield foo();
    
    console.log(result); 
    
}); // ==> 打印"OK"

我们看到,co函数接收一个Generator生成器函数作为参数。执行co函数的时候,生成器函数内部的逻辑像async函数调用时一样被执行。不同之处只是这里的await变成了yield

简单版本的co代码

要实现以上的逻辑,结合Generator的特性,co函数应该:

在函数体内将Generator生成器函数执行并生成生成器实例(在此命名为gen),然后通过gen.next方法的调用,不断执行生成器函数内部的代码。

执行next方法之后,返回的Promise在生成器函数执行环境之外执行,并取出resolve值,作为返回值作为next方法的参数返回到Generator执行环境中。

基于以上两点,我们可以大体实现一个简化版的co,代码如下:

const co = function(genFunc){  
    const gen = genFunc(); // 得到生成器实例  
    
    const deal = (val) => {
        
        const res = gen.next(val); 
        
        // 这里处理了异步逻辑,
        // 在回调中去递归,不断执行next
        // 这样就将resolve的值传回了Generator
        res.value.then(result => deal(result));
        
    }
    
    deal(); // 第一次触发递归
}

去掉括号等等,只有短短六行代码。

more

原理性的东西大约就是这样了。但是co做的不止这些。

之前coyield后的语句并不支持Promise对象,而是一个特殊的函数,叫做thunk。目前co二者都支持。
此处我并不打算重复性解释thunk版本,因为原理性的东西实现起来是差不多的。

co函数是有返回值的,也是一个Promise对象。

当生成器函数内的逻辑执行完毕且没有错误之后,这个Promise对象(co返回值)变为resolve状态,且将生成器的返回值作为resolve出来的值。

若生成器函数内返回一个Promise对象,那么co函数返回值就是这个Promise对象。

若生成器函数抛出了错误,那么这个错误作为reject出来的值,将Promise对象的状态变为reject

这样我们就可以将错误放进其返回值的.catch方法中统一处理。

在生成器函数内部,我们也可以使用try...catch语句获取错误对象。

生成器的yield后面可以跟一个元素值为Promise对象的数组,这个数组内Promise对象内的异步逻辑将并发执行,并返回一个数组。(类似于Promise.all方法)

假设生成器执行之前需要从外部传入参数,co库提供了一个方法:

  var fn = co.wrap(function* (val) {
  
     return yield Promise.resolve(val);

  });

  fn(true).then(function (val) {

  });
结束

以上是一点微小的见解。谢谢指正。

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

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

相关文章

  • JavaScript 异步

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...

    tuniutech 评论0 收藏0
  • 玩转Koa -- koa-bodyparser原理解析

    摘要:主要通过处理二进制数据流,但是它并不支持字符编码方式,需要通过模块进行处理。最后留图一张往期精彩回顾玩转原理解析玩转核心原理分析 一、前置知识   在理解koa-bodyparser原理之前,首先需要了解部分HTTP相关的知识。 1、报文主体   HTTP报文主要分为请求报文和响应报文,koa-bodyparser主要针对请求报文的处理。   请求报文主要由以下三个部分组成: 报文头...

    andycall 评论0 收藏0
  • 【全文】狼叔:如何正确的学习Node.js

    摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...

    Edison 评论0 收藏0
  • 【全文】狼叔:如何正确的学习Node.js

    摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...

    fengxiuping 评论0 收藏0

发表评论

0条评论

microelec

|高级讲师

TA的文章

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