Promise & Generator——幸福地用同步方法写异步JavaScript

最近在写一个本身的网站的时候(能够观摩一下~Colors),在无心识中用callback写了一段嵌套了5重回调函数的可怕的代码。回过神来的时候被本身吓了一跳,这可不行啊,丑得无法看啊!因而打算尝试一下一些流行的异步的解决方案。通过一番折腾以后...我终于找到了一个令本身满意的方案了(爱不释手)。不过在正式介绍它以前先扯一些其余的相关知识先吧!前端

1. JavaScript异步解决方案有哪一些

其实异步JavaScript已经不是什么高级的东西了,Nodejs的出现,特别是callback hell使人恐惧的写法已经成功倒逼出了不少很棒的解决方案。在这里看尤雨溪大神的这篇小短文,很是精简扼要地介绍了当前经常使用的async.js, Promise, co, async/await。我的建议有机会能够都试一下看看。而从我的的角度,我可能会以如下的标准来选择(我的喜爱):node

  1. 须要写爬虫之类控制并发数的我会用async.js;它的有一些API仍是很方便的。git

  2. 写前端的代码的时候可能会比较倾向于考虑Promise,由于通常来讲前端的异步场景除了ajax以外貌似也不是不少了。并且以前使用过isomorphic-fetch,感受很棒。能够看我以前的文章~es6

  3. 后端代码nodejs,那就非co莫属了。根据尤雨溪大神的说法,es7async/await也只是Promise & Generator的语法糖而已。而co,就是结合了PromiseGenerator的神通常的库。而本篇文章主要就是讲co结合PromiseGenerator的异步解决方法。github

2. Promise & Generator简单入门

ES6是个好东西,其中的PromiseGenerator能够说是精华的部分之一了。下面简单介绍入门一下Promise以及Generator。这一小节的介绍会很简单,并且也只是这两个新特性的一部分,可是提到的点都是本篇文章所须要的。固然,从学习的角度,应该找书去彻底了解一下这两个特性,起码有个印象吧~我的感受ES6的学习能够去读NCZUnderstanding ECMAScript6或者阮一峰大神的ES6标准入门,都有电子书,很棒!前者语言比较浅显易懂,生动有趣,后者会更加详细,有条理一些。若是您已经对这些特性了如指掌的话,那就不用看这一小节了~ajax

2.1 Promise

Promise有不少版本,也有不少实现的库,可是这里主要是介绍ES6标准的内容。若是阅读如下几条特性以为不懂的话建议先看看上面两本书相应的章节。数据库

  1. 关于promise,首先要意识到它是一种对象。这种对象能够用Promise构造函数来建立,也能够经过Nodejs自己一些默认的返回来获取这种对象。segmentfault

  2. promise对象有三种状态:PendingFulfilledRejected。分别对应着未开始的状态,成功的状态,以及失败的状态。后端

  3. 这种对象经常封装着异步的方法。在异步方法里面,经过resolvereject来划定何时算是成功,何时算是错误,同时传参数给这两个函数。这些参数就是异步获得的结果或者错误。promise

  4. 异步有成功的时候,也有错误的时候。对象经过thencatch方法来规定异步结束以后的操做(正确处理函数/错误处理函数)。而thencatchPromise.prototype上的函数,所以“实例化”以后(其实并不是真正的实例)能够直接使用。

  5. 这个promise对象还有一个神奇的地方,就是能够级联。每个then里面返回一个promise对象,就又像上面所提的那样,有异步就等待异步,而后选择出规定好的正确处理函数仍是错误处理函数。

2.2 Generator

Generator函数是一个带星星函数,并且是一个能够暂停的函数。

  1. 函数的内部经过yield来推动函数。经过定义yield后面的值来决定返回的value

  2. 函数返回一个遍历器,这个遍历器有一个next方法,能够获取一个对象,这个对象就包含了yield定义好的参数。

关于ES6的知识的其它特性就不谈了,对写同(yi)步代码的话掌握以上这些已经足够了。

3. Co

噔噔噔噔!神奇的Co登场了!这是一个tj大神写的库。使用方法很简单,在Github上的README也讲得很清楚了。主要就是两点:

  1. Co函数里面包裹一个generator函数,在generator函数里面能够yield promise对象,从而达到异步的目的。在Co的内部实现里面是经过递归调用next函数,把每个promise的值返回出来,从而实现异步转“同步”的写法。

  2. Co函数返回一个promise对象,能够调用thencatch方法来对Generator函数返回的结果进行传递。方便进行后续的成功处理或者错误处理。

4. 如何用同步的写法写异步的代码

下面展现一段异步处理的代码,能够看到,同步的写法写异步真的很爽...

function *foo(res, name, newPassword, oldPassword) {
  try {
    // yield一个promise对象,若是有错误就会被后面的catch捕捉到,成功就会返回user。
    const user = yield new Promise(function(resolve, reject) {
      // 常见的数据库读取星系
      User.get(name, function(err, user) {
        if(err) reject(err)
        resolve(user)
      })
    })

    if(user.password != oldPassword) {
      return res.send({errorMsg:"密码输入错误!"})
    }

    // 看到这一个异步函数和上一个的异步在写法上是基本上“同步”的,没有了相互嵌套,很优雅~也更加方便了debug~
    yield new Promise(function(resolve, reject) {
      User.update(name, newPassword, function(err) {
        if(err) reject(err)
        res.send({msg: "你成功更换密码了!"})
        resolve()
      })
    })

  } catch(e) {
    console.log("Error:", e)
    return res.send({errorMsg:"Setting Fail!"})
  }
}

// 使用的话就直接调用co包含对应的Generator函数便可。
co(foo(res, name, newPassword, oldPassword))

5. 总结

适合使用场景的方法才是最好的方法。可是当你在写Node的时候开始受到回掉地狱的困扰的时候,不妨尝试一下Co?用同步写法写异步的感受真的很不赖啊!

若是文中有某些地方有错误或者不稳当的地方,欢迎指出来,感激涕零!互相学习才能进步嘛~

相关文章
相关标签/搜索