最近在写一个本身的网站的时候(能够观摩一下~Colors),在无心识中用callback
写了一段嵌套了5重回调函数的可怕的代码。回过神来的时候被本身吓了一跳,这可不行啊,丑得无法看啊!因而打算尝试一下一些流行的异步的解决方案。通过一番折腾以后...我终于找到了一个令本身满意的方案了(爱不释手)。不过在正式介绍它以前先扯一些其余的相关知识先吧!前端
其实异步JavaScript
已经不是什么高级的东西了,Nodejs
的出现,特别是callback hell
使人恐惧的写法已经成功倒逼出了不少很棒的解决方案。在这里看尤雨溪大神的这篇小短文,很是精简扼要地介绍了当前经常使用的async.js
, Promise
, co
, async/await
。我的建议有机会能够都试一下看看。而从我的的角度,我可能会以如下的标准来选择(我的喜爱):node
须要写爬虫之类控制并发数的我会用async.js
;它的有一些API
仍是很方便的。git
写前端的代码的时候可能会比较倾向于考虑Promise
,由于通常来讲前端的异步场景除了ajax
以外貌似也不是不少了。并且以前使用过isomorphic-fetch
,感受很棒。能够看我以前的文章~es6
后端代码nodejs
,那就非co
莫属了。根据尤雨溪大神的说法,es7
的async/await
也只是Promise & Generator
的语法糖而已。而co
,就是结合了Promise
和Generator
的神通常的库。而本篇文章主要就是讲co
结合Promise
和Generator
的异步解决方法。github
ES6
是个好东西,其中的Promise
和Generator
能够说是精华的部分之一了。下面简单介绍入门一下Promise
以及Generator
。这一小节的介绍会很简单,并且也只是这两个新特性的一部分,可是提到的点都是本篇文章所须要的。固然,从学习的角度,应该找书去彻底了解一下这两个特性,起码有个印象吧~我的感受ES6
的学习能够去读NCZ
的Understanding ECMAScript6或者阮一峰大神的ES6标准入门,都有电子书,很棒!前者语言比较浅显易懂,生动有趣,后者会更加详细,有条理一些。若是您已经对这些特性了如指掌的话,那就不用看这一小节了~ajax
Promise
有不少版本,也有不少实现的库,可是这里主要是介绍ES6
标准的内容。若是阅读如下几条特性以为不懂的话建议先看看上面两本书相应的章节。数据库
关于promise
,首先要意识到它是一种对象。这种对象能够用Promise
构造函数来建立,也能够经过Nodejs
自己一些默认的返回来获取这种对象。segmentfault
promise
对象有三种状态:Pending
,Fulfilled
,Rejected
。分别对应着未开始的状态,成功的状态,以及失败的状态。后端
这种对象经常封装着异步的方法。在异步方法里面,经过resolve
和reject
来划定何时算是成功,何时算是错误,同时传参数给这两个函数。这些参数就是异步获得的结果或者错误。promise
异步有成功的时候,也有错误的时候。对象经过then
和catch
方法来规定异步结束以后的操做(正确处理函数/错误处理函数)。而then
和catch
是Promise.prototype
上的函数,所以“实例化”以后(其实并不是真正的实例)能够直接使用。
这个promise
对象还有一个神奇的地方,就是能够级联。每个then
里面返回一个promise
对象,就又像上面所提的那样,有异步就等待异步,而后选择出规定好的正确处理函数仍是错误处理函数。
Generator
函数是一个带星星函数,并且是一个能够暂停的函数。
函数的内部经过yield
来推动函数。经过定义yield
后面的值来决定返回的value
。
函数返回一个遍历器,这个遍历器有一个next
方法,能够获取一个对象,这个对象就包含了yield
定义好的参数。
关于ES6的知识的其它特性就不谈了,对写同(yi)步代码的话掌握以上这些已经足够了。
噔噔噔噔!神奇的Co
登场了!这是一个tj
大神写的库。使用方法很简单,在Github
上的README也讲得很清楚了。主要就是两点:
Co
函数里面包裹一个generator
函数,在generator
函数里面能够yield promise
对象,从而达到异步的目的。在Co
的内部实现里面是经过递归调用next
函数,把每个promise
的值返回出来,从而实现异步转“同步”的写法。
Co
函数返回一个promise
对象,能够调用then
,catch
方法来对Generator
函数返回的结果进行传递。方便进行后续的成功处理或者错误处理。
下面展现一段异步处理的代码,能够看到,同步的写法写异步真的很爽...
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))
适合使用场景的方法才是最好的方法。可是当你在写Node
的时候开始受到回掉地狱的困扰的时候,不妨尝试一下Co
?用同步写法写异步的感受真的很不赖啊!
若是文中有某些地方有错误或者不稳当的地方,欢迎指出来,感激涕零!互相学习才能进步嘛~