You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionco(gen){varctx=this;//如果是generatorFunction,就执行 获得对应的generator对象if(typeofgen==='function')gen=gen.call(this);//返回一个promisereturnnewPromise(function(resolve,reject){//初始化入口函数,第一次调用onFulfilled();//成功状态下的回调functiononFulfilled(res){varret;try{//拿到第一个yield返回的对象值retret=gen.next(res);}catch(e){//出错直接调用reject把promise置为失败状态returnreject(e);}//开启调用链next(ret);}functiononRejected(err){varret;try{//抛出错误,这边使用generator对象throw。这个的好处是可以在co的generatorFunction里面使用try捕获到这个异常。ret=gen.throw(err);}catch(e){returnreject(e);}next(ret);}functionnext(ret){//如果执行完成,直接调用resolve把promise置为成功状态if(ret.done)returnresolve(ret.value);//把yield的值转换成promise//支持 promise,generator,generatorFunction,array,object//toPromise的实现可以先不管,只要知道是转换成promise就行了varvalue=toPromise.call(ctx,ret.value);//成功转换就可以直接给新的promise添加onFulfilled, onRejected。当新的promise状态变成结束态(成功或失败)。就会调用对应的回调。整个next链路就执行下去了。if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);//否则说明有错误,调用onRejected给出错误提示returnonRejected(newTypeError('You may only yield a function, promise, generator, array, or object, '+'but the following object was passed: "'+String(ret.value)+'"'));}});}functionisPromise(obj){return'function'==typeofobj.then;}
koa源码分析系列(四)co-4.0新变化
koa基于co实现,co又是使用了es6的generator特性,所以,没错这个特性支持很一般。
有下面几种办法体验generator:
##核心代码分析
之前写过一篇co的源码分析文章,但是不久之后co就发生了重大变化,就是完全抛弃了thunk风格的函数。全部转用promise。于是,找了个时间我再次看了下源码。简单记录下。
本文假设你已经熟悉了es6里面promise的基本用法。如果不是特别清楚的可以参考下面几篇文章:
co4.0全部采用promise来实现。下面我们分析下代码。
首先co的用法发生了改变:
可以看到co还是接受了一个generatorFunction作为参数,实际上参数如果是一个generator对象也是可以的。如果是generatorFunction,co内部会帮你执行生成对应的generator对象。
不同的是co不再返回一个thunk函数,而是返回了一个promise对象。
yield后面推荐的也是promise对象,而不是thunk函数了。
我们看下实现:
核心代码主要是onFulfilled与next的实现。
我们先不考虑错误处理看下执行流程。也先不看toPromise的实现。假定我们只是yield一个promise对象。
例子:
假设:
Promise.resolve('传给a的值');
生成的叫做promise对象A。Promise.resolve('传给b的值');
生成的叫做promise对象B。onFulfilled作为入口函数。
yield Promise.resolve('传给a的值');
然后停住。拿到了返回值`{value:'promise对象A',done:false}。yield Promise.resolve('传给b的值');
停住。不同的是这次调用onFulfilled会传递res的值。通过gen.next(res)会把res也就是resolve的值赋值给a。然后继续这个过程,一直到最后return的时候。
这样整个调用链就执行下去了。可以看到主要是使用promise的then方法添加onfullied操作函数,来实现自动调用
gen.next()
。##co的错误处理
co的错误处理主要使用onRejected实现,基本逻辑跟onFulfilled差不多,这边主要说一下
gen.throw(err);
的原理。generator对象的一个特性是可以在generatorFunction外面抛出异常,在generatorFunction里面捕获到这个异常。
当我们运行gen.next()的时候,会运行到yield 'a'这一句。这一句正好在内部的try范围内,因此
g.throw('外面报错消息')
这个抛出的错误会被捕获到。如果我们不调用gen.next()或者连续调用三次gen.next()。代码执行不在try的范围,这个时候去gen.throw错误就不会被内部捕获到。
所以co里面用了这个特性,可以让你针对某一个或多个yield加上try,catch代码。
co发现某个内部promise报错就会调用onRejected然后调用gen.throw抛出错误。
如果你不处理错误,co就调用reject(err)传递给包装后的co返回的promise对象。这样你就可以在co(*fn).catch 拿到这个错误。
##toPromise的实现
我们看下toPromise的代码:
主要就是各种判断,把不同类型的yield值转换成一个promise对象。
前面几个都很简单不说了。
thunkToPromise比较简单如下:
arrayToPromise也比较容易:
objectToPromise会稍微绕一点:
objectToPromise的主要思路是循环递归遍历对象的值
##结语
整个分析到这就结束了,新版的co代码非常清晰也更加容易理解。不过完全抛弃thunk不知道TJ大神怎么想的。好像目前的koa还是使用的老的co来实现的。不管怎么说,还是值得看一看的。
The text was updated successfully, but these errors were encountered: