ES6+ Generator 函数 应用
1. 前言
上一节我们注意学习了 生成 器的概念和基本 用法 ,并通过两个案例来说明。但是 生成 器更加广泛和设计之初是为了 解决 异步而产生的。我们会通过 一个 开发中常见的问题入手来看 生成 器 函数 到底是怎么来 解决 异步 调用 的问题,并且会实现 一个 简版的 co 库。
2. 案例
在开发过程中会遇到 一个 很常见的需求,我们想 获取 一个 值,但不能直接拿到,我们只能先请求 一个 接口如:api_1, 获取 这个值的接口地址如:api_2。然后,请求 api_2 接口才能 获取 这个值。这是 一个 典型的需要异步回调才能完成的 功能 。
在学习 Promise 的时候我们也针对这样的情况,我们可以使用 Promise 来完成这样的 功能 :
var promise = function ( url ) { return new Promise ( ( resolve , reject ) => { ajax ( url , ( data ) => { resolve ( data ) // 成功 } , ( error ) => { reject ( error ) // 失败 } ) } ) } promise ( 'api_1' ) . then ( res1 => { promise ( res1 ) . then ( res2 => { console . log ( res2 ) } ) } )
从上面的 代码 中可以看出, 在这 种情况下,使用 Promise 好像并没有 解决 回调地狱的问题。那如何 解决 这种问题呢?我们想到了 Generator 函数 具有暂停 功能 ,那是不是我们可以让请求 api_2 接口时暂停,等到 api_1 请求成功 获取 到地址后再去请求呢?按照这个思路我们可以有下面的 代码 :
const ajax = function ( api ) { return new Promise ( ( resolve , reject ) => { setTimeout ( ( ) => { if ( api === 'api_1' ) { resolve ( 'api_2' ) ; } if ( api === 'api_2' ) { resolve ( ) ; } } , ) } ) } function * getValue ( ) { const api = yield ajax ( 'api_1' ) ; const value = yield ajax ( api ) ; return value ; } console . log ( getValue ( ) ) ; // Object [Generator] {}
上面的 代码 是我们模拟 ajax 请求,通过使用 生成 器 函数 写出的 代码 让我们感觉有了同步的感觉,但是这样去执行 getValue 函数 是不会得到结果的。那么我们要怎样去获得结果呢?根据 生成 器 函数 的特点,可以这样写:
let it = getValue ( ) ; let { value } = it . next ( ) ; value . then ( ( data ) => { let { value } = it . next ( data ) ; value . then ( ( data ) => { Console . log ( data ) ; } ) ; } ) ;
从上面的 代码 中看出还是有嵌套,好像并没有 解决 问题。但如果你细心,你会发现每个回调的逻辑基本都是一样的。那么我们能不能对这样的嵌套 函数 进行封装呢?答案当然是可以的,有个库就专门 解决 了这个痛点 —— co 库,有兴趣的可以去研究一下这个库, 代码 很少,下面我们就来封装 一个 这样的库。
先让我们看看 co 库是怎么使用的:
co ( getValue ( ) ) . then ( res => { console . log ( res ) ; } )
从上面的 代码 中可以看出,把 函数 传入进去,并让 函数 执行,然后在 then 的成功回调中可以 获取 @H_201_ 502 @getValue 函数 返回的最终结果。这样非常清晰地 解决 了上面我们需要手动执行的 方法 ,下面我来分析一下具体的实现步骤:
从上面的 用法 可以看出,co 返回的是 一个 Promise 实例,所以我们需要返回 一个 @H_201_ 502 @new Promise() 实例; 传入的 生成 器 函数 执行后,我们可以 调用 next () 函数 拿到返回的值和是否执行完的状态,判断 done 如果是 true 时说明执行完了,可以执行 resolve; 当 生成 器 函数 没有执行完时,这时我们就需要递归地去 调用 这个 next () 来执行下一步,因为传入的值是 一个 Promise 实例,要想 获取 它的结果就需要链式 调用 then 方法 ,然后拿到结果进行递归执行。
有了上面的步骤分析,不难得到下面的 代码 :
function co ( it ) { return new Promise ( ( resolve , reject ) => { function next ( data ) { let { value , done } = it . next ( data ) ; if ( done ) { resolve ( value ) ; } else { Promise . resolve ( value ) . then ( data => { next ( data ) ; } , reject ) } } next ( undefined ) ; } ) }
上面的 代码 中需要注意的是,如果 yield 返回的不是 一个 Promise 对象时,我们对 value 使用了 @H_201_ 502 @Promise.resolve() 进行了包装,这样就可以处理返回 一个 普通值时没有 then 方法 的问题。
3. 小结
本节主要讲解了 Generator 函数 在异步中的应用, 解决 了某些场景下还会产生回调地狱的问题,通过封装 co 方法 让我们的 代码 写起来像是同步一样,但是 Generator 函数 还不是我们 解决 异步的终极方案,下一节我们将学习 async 函数 ,看它是怎么来 解决 异步的。
查看更多关于ES6+ Generator 函数应用的详细内容...