好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ES6+ Generator 函数应用

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 函数应用的详细内容...

  阅读:39次

上一篇

下一篇

第1节:ES6+ 简介    第2节:ES6 环境配置    第3节:ES6+ let    第4节:ES6+ const    第5节:ES6+ 展开语法    第6节:ES6+ 剩余参数    第7节:ES6+ 解构赋值    第8节:ES6+ 模版字符串    第9节:ES6+ 箭头函数    第10节:ES6+ 数值扩展    第11节:ES6+ isFinite()&isNaN()    第12节:ES6+ Number 对象的方法    第13节:ES6+ Math 对象的扩展    第14节:ES6+ includes()    第15节:ES6+ 字符串的扩展    第16节:ES6+ startsWith()    第17节:ES6+ endsWith()    第18节:ES6+ repeat()    第19节:ES6+ padStart()    第20节:ES6+ padEnd()    第21节:ES6+ trim()    第22节:ES6+ Array.from()    第23节:ES6+ of()    第24节:ES6+ find()和findIndex()    第25节:ES6+ copyWithin()    第26节:ES6+ fill()    第27节:ES6+ isArray()    第28节:ES6+ 对象的扩展    第29节:ES6+ flat()    第30节:ES6+ 可选链操作符    第31节:ES6+ Object.is()    第32节:ES6+ Object.assign()    第33节:ES6+ Object.keys()    第34节:ES6+ Object.values()    第35节:ES6+ Object.entries()    第36节:ES6+ 数据结构扩展    第37节:ES6+ Set    第38节:ES6+ WeakSet    第39节:ES6+ Map    第40节:ES6+ WeakMap    第41节:ES6+ Symbol    第42节:ES6+ for...of    第43节:ES6+ 迭代协议    第44节:ES6+ 实现一个简版的 Promise    第45节:ES6+ Promise 基础    第46节:ES6+ Promise 进阶    第47节:ES6+ Generator 基础    第48节:ES6+ Generator 函数应用    第49节:ES6+ async/await    第50节:ES6+ Class 前置知识    第51节:ES6+ Class    第52节:ES6+ Proxy    第53节:ES6+ Reflect(一)    第54节:ES6+ Reflect(二)    第55节:ES6+ 模块化(一)    第56节:ES6+ 模块化(二)    第57节:ES6实战1-实现Vue3 reactive 源码    第58节:ES6实战2-实现 Vue3 effect 源码    第59节:ES6 实战2-封装请求    第60节:ES6+ 实战3-代码整洁之道    第61节:ES6 Map原理分析    第62节:ES6module语法加载importexport    第63节:ES6的循环与可迭代对象示例详解