过去一年里我把node 6升级到 8 (开发环境下甚至鼓励大家用最新的node 10),一个主要目的就是为了解决callback hell的问题.
回调地狱的问题我不是第一次碰到,在做iOS开发时候,使用AFN就有这个“Pyramid of doom”的问题,当时的解决办法,也是后来用swift要求大家尽量用guard,说白了就是early return 但这个方法对nodejs的回调地狱不适用(用命名函数可以部分解决)
刚开始接触node,对promise还不熟悉,为了解决callback hell 首先引入的是 async这个模块 但是用async总感觉不是很自然, boilerplate code也不少。另,没有用其他的库,比如thenjs原因很简单:<1> 它的star数量和async不能比,这是我选开源库一个最重要参考 <2>知道以后肯定会用promise,不想再引入一个“似是而非”的then
promise chain是在熟悉了node之后很自然会引入的第二个解决方案,关于promise chain这篇文章 We have a problem with promises 一定要读透。
MDN对promise的讲解也很到位、全面。如果还觉得不够,那就看看javascript.info的几篇文章promise async/await, rethrow
要点其实也简单,每个then里一定记得要return, 如果直接return值,这个值会被包裹成promise.resolve的值
我自己理解这些概念还是花了点时间,在SO提了一个问题,得到很好解答
要写好promise chain并不是一件容易的事,debug也是个大问题,因为没法对then设置断点。 所以当知道node 8已经很好支持async/await后我决定把一些原来用 promise chain写的代码改为 async/await,一方面熟悉async/await为以后开发做准备;一方面也是为了review下原先没机会review的代码。
其实我们有些模块已经是用 co 但因为知道TJ大神退出node好多年 co库三年多都不更新,所以也借着这个机会一并全都换了
使用async/await本身很简单(这也是必然的,因为它的出发点就是让异步代码像同步代码),只是有几点要注意:
- 不需要串行执行的代码就用 Promise.all
- 如果某一步出错了,如何退出? 特意在SO提了这个问题await Promise.reject or throw error to bail out? 我比较倾向 throw
- 原来用 async module代码在引入async/await要怎么改? 还好async也支持 async/await, 把原来callback直接改成return就是
Async accepts async functions wherever we accept a Node-style callback function. However, we do not pass them a callback, and instead use the return value and handle any promise rejections or errors thrown.
- async/await 性能问题、bluebird还要吗,可不可以就用node的知道promise. 这个问题应该是真的碰到性能瓶颈才需要花时间去调研的问题。但实现知道一些大致要点也不错。这里有一份简单比较,总的来说bluebird依旧需要
后记,如何升级node版本
node版本管理工具就两个 nvm 和 n,详细的使用上的区别可以看这篇 简单的说,nvm真的是比较麻烦。
对我们来说不需要频繁切换版本,就是要升级下一个LTS版本,而且比起nvm的安装, 执行sudo npm install -g n
简单多了,所以自然是选用n.
在Ubuntu上,通过下载Linux Binaries 升级并不是好办法 ,如果已经有了一个老版本的node,选用n是最方便的办法 ('The irony is not lost on me that you must first install node in order to install the node manager.')。
另,在Ubuntu上 全新的安装可以参见这里