背景:

闲来无事,翻了下co的源码来看,源码短小精悍,算上注释,一共240行左右;

决定写一篇博客来记录下学习的心得。

TJ大神的co:https://github.com/tj/co

作用:

co通过将Generator函数拆成一个Promise将码农从callback hell中拯救了出来;

下边放出一段代码,对比下co与普通回调版本的区别:

 /**
* 回调版本
*/ let fs = require('fs') fs.readFile('./package.json', (err, data) => {
if (err) {
return console.log(err)
}
console.log(data.toString())
fs.readFile('./package.json', (err, data) => {
if (err) {
return console.log(err)
}
console.log(data.toString())
})
}) /**
* co版本
*/ let co = require('co')
let fs = require('fs') co(function * () {
let a = yield fs.readFile.bind(null, './package.json')
console.log(a.toString())
let b = yield fs.readFile.bind(null, './package.json')
console.log(b.toString())
}).then(console.log, console.error)

从代码上看,貌似co是一个同步执行的过程呢。当然,也只是看起来像而已。

正题:

先来说一下co整个执行的过程:

  • 调用co,传入一个Generator函数,函数会返回一个Promise对象
  • 如果传入参数为Generator函数,会执行该函数来进行Generator的初始化
  • 手动执行一次next() 这时Generator函数就会停在第一次遇到yield关键字的地方
  • 获取到yield后边的值,将其转换为一个Promise函数,然后执行之
  • 重复上边两步,直到函数执行完毕

co关于yield后边的值也是有一定的要求的,只能是一个 Function|Promise|Generator | Array | Object;

而 Array和Object中的item也必须是 Function|Promise|Generator。

并且关于function 普通函数并不一定会得到预期的结果,co需要的是 接收一个回调函数 并执行的函数,类似于这样:

 function doSomething (callback) {
callback(null, 'hello')
}
co(function * () {
let result = yield doSomething
console.log(result) // => hello
})

总而言之,co执行的肯定是一个Promise,而co会帮你把其他几种类型的值转换为Promise,co绝大部份的代码都是在处理类型的转换;

当然,在讲类型转换的那一块之前,还是将co执行Generator的那几个函数说一下子,也就是调用co返回的Promise中的那三个函数(onFulfilled、onRejected、next);

因next与Generator对象的next方法名相同 这里使用 gen.next 表示 Generator对象的next方法。

onFulfilled:

调用gen.next并将上次执行的结果传入gen.next;

调用next,将gen.next返回的值传入next。

onRejected:

执行流程与 onFulfilled 一致,只不过是将调用的 gen.next 换为了 gen.throw 用来将错误异常抛出。

next:

函数会判断传入参数的done属性,如果为true( 则表示该Generator已经执行完毕),会调用co返回的Promise对象的resolve方法,结束代码执行;

如果done为false 则表示还需要继续执行,这里会将 yield后边的值(参数的value属性)转换为Promise,并调用then方法传入 onFulfilled 和 onRejected两个函数。

co整个的执行流程其实就是这样的-.-

剩余代码所完成的事情就是将各种不同的类型转换为可执行的Promise对象。

thunkToPromise(Function):

函数返回一个Promise对象,在Promise内部执行了传入的function;

并会认为回调的第一个参数为Error(这个貌似是个标准...);

将其余参数打包到一个数组中返回。

arrayToPromise(Array):

Promise有一个方法叫做all,会返回数组中所有Promise执行后的返回值(如果有其中一项被reject掉,所有的都会被reject);

方法会返回 Promise.all() 的执行结果

 Promise.all([
Promise.resolve('hello'),
Promise.resolve('world')
]).then(data => {
console.log(data) // => ['hello', 'world']
})

objectToPromise(Object):

函数用来将一个Object对象转换为Promise;

应该是co源码中行数最多的一个函数了

co源码解读的更多相关文章

  1. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  2. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  3. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  4. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  5. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  6. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  7. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  8. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  9. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  10. AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking

    AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...

随机推荐

  1. Oracle使用触发器和mysql中使用触发器的比较——学习笔记

    一.触发器 1.触发器在数据库里以独立的对象存储, 2.触发器不需要调用,它由一个事件来触发运行 3.触发器不能接收参数 --触发器的应用 举个例子:校内网.开心网.facebook,当你发一个日志, ...

  2. Linux.NET学习手记(7)

    前一篇中,我们简单的讲述了下如何在Linux.NET中部署第一个ASP.NET MVC 5.0的程序.而目前微软已经提出OWIN并致力于发展VNext,接下来系列中,我们将会向OWIN方向转战. 早在 ...

  3. AutoMapper(五)

    返回总目录 Dynamic和ExpandoObject映射 AutoMapper不用任何配置就可以从dynamic(动态)对象映射或映射到dynamic对象. namespace FifthAutoM ...

  4. Visual Studio 实用扩展推荐

    Visual Studio 拥有非常不错的可扩展性,在之前的文章中,我也给大家示范了如何进行编辑器的扩展(详见文末参考资源).在本篇文章中,我将介绍几款非常实用的扩展,从而帮助我们提高开发效率. C# ...

  5. 使用xUnit,EF,Effort和ABP进行单元测试(C#)

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 本篇目录 介绍 创建测试项目 准备测试基类 创建第一个测试 测试异常 在测试中使用仓储 测试异步方法 小结 介绍 在这篇博客中,我 ...

  6. C++ std::map

    std::map template < class Key, // map::key_type class T, // map::mapped_type class Compare = less ...

  7. clearfix的最佳方案----在路上(22)

    clearfix的纠结 骨灰级解决办法: .clear{clear:both;height:0;overflow:hidden;} 上诉办法是在需要清除浮动的地方加个div.clear或者br.cle ...

  8. 应用新安全组 - 每天5分钟玩转 OpenStack(116)

    Neutron 默认的安全组规则会禁止掉所有从外面访问 instance 的流量. 本节我们会修改安全组的配置,允许 ping 和 ssh instance.有两种方法可以达到这个目的: 1. 修改 ...

  9. SQL Tuning 基础概述08 - SQL Tuning Advisor

    SQL调优顾问 SQL Tuning Advisor的使用案例: 1.构建测试表T 2.定义调整任务 3.修改调整任务参数 4.执行调整任务 5.监控调整任务 6.查看调整任务建议 7.删除调整任务 ...

  10. 【分布式】Zookeeper序列化及通信协议

    一.前言 前面介绍了Zookeeper的系统模型,下面进一步学习Zookeeper的底层序列化机制,Zookeeper的客户端与服务端之间会进行一系列的网络通信来实现数据传输,Zookeeper使用J ...