javascript把生成器和promise相结合

60 篇文章 0 订阅
55 篇文章 1 订阅

结合生成器(以及生成器暂停和恢复执行的能力)和promise,来实现更加优雅的异步代码。

try{
  const ninjas = syncGetJSON('data/ninja.json');
  const missions = syncGetJSON(ninjas[0].missionsUrl);
  const missionDetails = syncGetJSON(missions[0].detailsUrl);
  //Study the mission description
} catch (e) {
  //Oh no,we were not able to get the mission details
}

尽管这段代码对于简化错误处理很方便,但UI被阻塞了,用户不希望看到这个结果。则可以使用生成器和promise结合。从生成器中让渡后会挂起执行而不会发生阻塞。而且仅需调用生成器迭代器的next方法就可以唤醒生成器并继续执行。而promise在未来触发某种条件的情况下让我们得到它事先允诺的值,而且当错误发生后也会执行相应的回调函数。

这个方法将要以如下方式结合生成器和promise。

把异步任务放入一个生成器中,然后执行生成器函数。所以生成器执行的时候,我们会将执行权让渡给生成器,从而不会导致阻塞。过一会儿,当承若被兑现,我们会继续通过迭代器的next函数执行生成器。只要有需要就可以重复这个过程。

 

console.log('-------------将promise和生成器结合---------');
//返回异步结果的函数在等待异步结果返回时应当能够暂停,注意function*,使用生成器
async(function* () {
  try{
    //对每个任务均执行yield
    const ninjas = yield getJSON('data/ninjas.json');
    const missions = yield getJSON(ninjas[0].missionsUrl);
    const missionDescription = yield getJSON(missions[0].detailsUrl);
    //Study the mission description
  } catch (e) {
    //依旧可以使用标准的语言结构,诸如try-catch语句或循环语句
    //Oh no,we were not able to get the mission details
  }
});

//定义一个辅助函数,用于对我们定义的生成器执行操作
function async(generator) {
  //创建 一个迭代器,进而我们可以控制生成器
  var iterator = generator();

  //定义函数handle,用于对生成器产生的每个值进行处理。
  function handle(iteratorResult) {
    //当生成器没有更多结果返回时停止执行。
    if (iteratorResult.done) {
      return;
    }

    const iteratorValue = iteratorResult.value;
    //如果生成的值是一个promise,则对其注册成功和失败的回调。这是异步处理的部分。如果promise成功返回,则恢复生成器的执行并传入promise的返回结果。如果遇到错误,则生成器抛出异常。
    if (iteratorValue instanceof Promise) {
      iteratorValue.then(res =>{
        handle(iterator.next(res));
      }).catch(err =>{
        iterator.throw(err);
      });
    }
  }
 
  //重启生成器的执行
  try{
    handle(iterator.next());
  } catch (e) {
    iterator.throw(e);
  }
}

 

async函数内,我们声明了一个处理函数用于处理从生成器中返回的值——迭代器的一次“迭代”。如果生成器的结果是一个被成功兑现的承若,我们就是用迭代器的next方法把承诺的值返回给生成器并恢复执行。如果出现错误,承若被违背,我们就使用迭代器的throw方法抛出一个异常。直到生成器完成前,一直重复这几个操作。

注意:

这只是一个粗略的草稿,一个最小化的代码应该把生成器和promise结合在一起。不推荐生产环境下出现这些代码。

现在仔细看看这个生成器,在第一次调用迭代器的next方法后,生成器执行第一次getJSON('data/ninjas.json')调用。此次调用创建了一个promise,该promise会包含需要的信息。但是这个值是异步获取的,所以我们完全不知道浏览器会花多少时间来获取它。但我们明白一件事,我们不希望在等待中阻塞应用的执行。所以对于这个原因,在执行的这一刻,生成器让渡了控制权,生成器暂停,并把控制流还给了回调函数的执行。由于让渡的值是一个promise对象getJSON,在这个回调函数中,通过使用promise的then和catch方法,我们注册了一个success和一个error回调函数,从而继续了函数的执行。当浏览器接收到了响应(可能是成功的响应,也可能是失败的响应),promise的两个回调函数之一则被调用了。如果promise被成功解决,则会执行success回调函数,随之而来的是迭代器next方法的调用,用于向生成器请求新的值,从而向生成器请求新值,从而生成器从挂起状态恢复,并把得到的值回传给回调函数。这意味着,程序又重新进入到生成器函数体内,当第一次执行yield表达式后,得到的值变成从服务器端获取的信息。

下一行代码的生成器函数中,我们使用获取到的数据ninjas[0].missionsUrl来发起新的geJSON请求,从而创建一个新的promise对象,最后返回最新的数据。我们依然不知道这个异步任务会执行多久,所以我们再一次让渡了这次执行,并重复这个过程。只要生成器中有异步任务,这个过程就会重新执行一次。

这个例子有一点不同,但它结合了许多知识点:

1.函数是第一类对象——我们向async函数了一个参数,该参数也是函数。

2.生成器函数——用它的特性来挂起和恢复执行。

3.pomise——帮我们处理异步代码。

4.回调函数——在promise对象上注册成功和失败的回调函数。

5.箭头函数——箭头函数的简洁适合用在回调函数上。

6.闭包——在我们控制生成器的过程中,迭代器在async函数内被创建,随之我们在promise的回调函数内通过闭包来获取该迭代器。

 

逻辑代码的书写如下方式:

getJSON("data/ninjas.json", (err, ninjas) =>{
  if (err) {
    console.log('Error fetchig ninjas', err);
    return;
  }

  getJSON(ninjas[0].missingsUrl, (err, missions) =>{
    if (err) {
      console.log("Error locating ninja missions", err);
      return;
    }
    console.log(missions);
  })
});

 

不同于把错误处理和流程中控制混合在一起,我们使用类似以下写法结束了代码的混乱:

async(function* () {
  try{
    //对每个任务均执行yield
    const ninjas = yield getJSON('data/ninjas.json');
    const missions = yield getJSON(ninjas[0].missionsUrl);
    //Study the mission description
  } catch (e) {
    //依旧可以使用标准的语言结构,诸如try-catch语句或循环语句
    //Oh no,we were not able to get the mission details
  }
});

 

最终结果结合了同步代码和异步代码的优点。有了同步代码,我们能更容易地理解、使用标准控制流以及异常处理机制、try-catch语句能力。而对于异步代码来说,我们有着天生的阻塞:当等待长时间运行的异步任务时,应用的执行不会被阻塞。

 

参考《JavaScriipt忍者秘籍》

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值