最近在写自己的网站(大家可以看看~Colors)的时候,无意中写了一个可怕的嵌套5重回调函数,里面有回调代码。等我回过神来,被自己吓了一跳,这不行,太难看了!所以我打算尝试一些流行的异步解决方案。折腾了好久……终于找到了让我满意的方案(爱不释手)。不过在正式介绍之前,还是先说说其他的一些相关知识吧!1.JavaScript异步解决方案有哪些?其实异步JavaScript已经不是什么高深的东西了。Nodejs的出现,尤其是callbackhell这种吓人的写法,成功倒逼了很多很棒的解决方案。这里有游雨溪的一篇小文,非常简洁的介绍了目前常用的async.js、Promise、co、async/await。我个人的建议是有机会的话去试试看。从个人角度来说,我可能会根据以下标准(个人喜好)来选择:如果需要写爬虫之类的来控制并发数,我会用async.js;它有一些API还是很方便的。在写前端代码的时候,大家可能更倾向于考虑Promise,因为一般来说,除了ajax,前端异步的场景似乎并不多。而且之前用过isomorphic-fetch,感觉很棒。可以看看我之前的文章~后端代码nodejs,那肯定是co。按照尤雨熙的说法,es7的async/await只是Promise&Generator的语法糖。而co是一个结合了Promise和Generator的神级库。而这篇文章主要讲的是Promise和Generator协同组合的异步方案。2.简单介绍一下Promise&GeneratorES6是个好东西,其中Promise和Generator可以说是最好的部分之一。下面简单介绍一下Promise和Generator。这部分的介绍会很简单,只是这两个新特性的一部分,但是提到的点都是本文需要的。当然,从学习的角度来说,你应该找一本书来全面了解这两个特性,至少有个印象~个人觉得ES6学习可以看NCZ的UnderstandingECMAScript6orRuanYifeng'sintroductiontotheES6standard,两者里面有电子书,太棒了!前者的语言比较通俗易懂,生动有趣,而后者会比较细化和条理化。如果你已经熟悉这些特性,那这节就不用看了~2.1PromisePromise有很多版本和很多实现库,但这里主要介绍ES6标准的内容。如果您不了解以下功能,建议先阅读以上两本书的相应章节。关于承诺,首先要认识到它们是一种对象。这种对象可以通过Promise构造器来创建,也可以通过Nodejs自身的一些默认返回来获取。promise对象具有三种状态:Pending、Fulfilled和Rejected。分别对应未启动状态、成功状态、失败状态。此类对象通常封装异步方法。在异步方法中,通过resolve和reject来判断什么时候认为成功,什么时候认为错误,同时给这两个函数传递参数。这些参数是异步获取的结果或错误。异步有成功的时候,也有错误的时候。对象通过then和catch方法指定异步结束后的操作(正确处理函数/错误处理函数)。而then和catch是Promise.prototype上的函数,所以它们可以在“实例化”(不是真正的实例)之后直接使用。这个promise对象还有一个神奇的地方,就是可以级联。每个then返回一个promise对象,并且如前所述,如果有异步,则等待异步,然后选择指定的正确处理函数或错误处理函数。2.2GeneratorGenerator函数是一个带星号的函数,是一个可以挂起的函数。函数内部通过yield来推进函数。返回值由定义yield后的值决定。函数返回一个遍历器,它有一个next方法,可以得到一个对象,这个对象包含yield定义的参数。ES6的其他特性知识我就不说了,但是对于写同(yi)步代码,掌握以上就够了。3.扑通扑通!神奇的公司来了!这是tjmaster写的库。使用方法很简单,Github上的README也很清楚。主要有两点:在Co函数中包裹了一个生成器函数,在生成器函数中可以yield一个promise对象,从而达到异步的目的。Co内部实现中,递归调用next函数返回每个promise的值,从而实现异步到“同步”的写法。Co函数返回一个promise对象,可以调用then和catch方法传递Generator函数返回的结果。方便后续成功处理或错误处理。4.如何使用同步写法来写异步代码下面展示了一段异步处理代码。可以看到同步写异步真的很爽...function*foo(res,name,newPassword,oldPassword){try{//yield一个promise对象,如果有错误,会被catch捕获稍后,如果成功,它将返回给用户。constuser=yieldnewPromise(function(resolve,reject){//普通数据库读取galaxyUser.get(name,function(err,user){if(err)reject(err)resolve(user)})})if(user.password!=oldPassword){returnres.send({errorMsg:"密码输入错误!"})}//看到这个异步函数和前面的异步函数在写法上基本是“同步”的,没有相互嵌套,很优雅~也更方便debug~send({msg:"你修改密码成功了!"})resolve()})})}catch(e){console.log("Error:",e)returnres.send({errorMsg:"设置失败!"})}}//如果使用,直接调用co就可以包含对应的Generator函数。co(foo(res,name,newPassword,oldPassword))5.最好的方法是总结适合使用场景的方法。但是当你开始为写Node时掉进地狱而烦恼时,为什么不试试Co呢?异步写同步写的感觉真好!文中如有错误或不当之处,请指出,万分感谢!只有互相学习才能进步~
