构建一个应用程序老是会面对异步调用,不管是在 Web 前端界面,仍是 Node.js 服务端都是如此,JavaScript 里面处理异步调用一直是很是恶心的一件事情。之前只能经过回调函数,后来渐渐又演化出来不少方案,最后 Promise 以简单、易用、兼容性好取胜,可是仍然有很是多的问题。其实 JavaScript 一直想在语言层面完全解决这个问题,在 ES6 中就已经支持原生的 Promise,还引入了 Generator 函数,终于在 ES7 中决定支持 async 和 await。前端
async/await 到底是怎么解决异步调用的写法呢?简单来讲,就是将异步操做用同步的写法来写。先来看下最基本的语法(ES7 代码片断):node
const f = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(123); }, 2000); }); }; const testAsync = async () => { const t = await f(); console.log(t); }; testAsync();
首先定义了一个函数 f
,这个函数返回一个 Promise,而且会延时 2 秒,resolve
而且传入值 123。testAsync
函数在定义时使用了关键字 async
,而后函数体中配合使用了 await
,最后执行 testAsync
。整个程序会在 2 秒后输出 123,也就是说 testAsync
中常量 t
取得了 f
中 resolve
的值,而且经过 await
阻塞了后面代码的执行,直到 f
这个异步函数执行完。git
仅仅是一个简单的调用,就已经可以看出来 async/await 的强大,写码时能够很是优雅地处理异步函数,完全告别回调恶梦和无数的 then
方法。咱们再来看下与 Promise 的对比,一样的代码,若是彻底使用 Promise 会有什么问题呢? github
const f = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(123); }, 2000); }); }; const testAsync = () => { f().then((t) => { console.log(t); }); }; testAsync();
从代码片断中不难看出 Promise 没有解决好的事情,好比要有不少的 then
方法,整块代码会充满 Promise 的方法,而不是业务逻辑自己,并且每个 then
方法内部是一个独立的做用域,要是想共享数据,就要将部分数据暴露在最外层,在 then
内部赋值一次。虽然如此,Promise 对于异步操做的封装仍是很是不错的,因此 async/await
是基于 Promise 的,await
后面是要接收一个 Promise 实例。npm
RxJS 也是很是有意思的东西,用来处理异步操做,它更能处理基于流的数据操做。举个例子,好比在 Angular2 中 http 请求返回的就是一个 RxJS 构造的 Observable Object,咱们就能够这样作:gulp
$http.get(url) .map(function(value) { return value + 1; }) .filter(function(value) { return value !== null; }) .forEach(function(value) { console.log(value); }) .subscribe(function(value) { console.log('do something.'); }, function(err) { console.log(err); });
若是是 ES6 代码能够进一步简洁:浏览器
$http.get(url) .map(value => value + 1) .filter(value => value !== null) .forEach(value => console.log(value)) .subscribe((value) => { console.log('do something.'); }, (err) => { console.log(err); });
能够看出 RxJS 对于这类数据能够作一种相似流式的处理,也是很是优雅,并且 RxJS 强大之处在于你还能够对数据作取消、监听、节流等等的操做,这里不一一举例了,感兴趣的话能够去看下 RxJS 的 API。服务器
这里要说明一下的就是 RxJS 和 async/await 一块儿用也是能够的,Observable Object 中有 toPromise
方法,能够返回一个 Promise Object,一样能够结合 await
使用。固然你也能够只使用 async/await 配合 underscore 或者其余库,也能实现很优雅的效果。总之,RxJS 与 async/await 不冲突。babel
经过使用 async/await,咱们就能够配合 try/catch 来捕获异步操做过程当中的问题,包括 Promise 中 reject 的数据。 app
const f = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(234); }, 2000); }); }; const testAsync = () => { try { const t = await f(); console.log(t); } catch (err) { console.log(err); } }; testAsync();
代码片断中将 f
方法中的 resolve
改成 reject
,在 testAsync
中,经过 catch
能够捕获到 reject
的数据,输出 err 的值为 234。try/catch
使用时也要注意范围和层级。若是 try
范围内包含多个 await
,那么 catch
会返回第一个 reject
的值或错误。
const f1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(111); }, 2000); }); }; const f2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(222); }, 3000); }); }; const testAsync = () => { try { const t1 = await f1(); console.log(t1); const t2 = await f2(); console.log(t2); } catch (err) { console.log(err); } }; testAsync();
如代码片断所示,testAsync
函数体中 try
有两个 await
函数,并且都分别 reject
,那么 catch
中仅会触发 f1
的 reject
,输出的 err 值是 111。
不管是 Web 前端仍是 Node.js 服务端,均可以经过预编译的手段实现使用 ES6 和 ES7 来写代码,目前最流行的方案是经过 Babel 将使用 ES七、ES6 写的代码编译为 E6 或 ES5 的代码来执行。
服务端使用 Babel,最简单的方式是经过 require
hook。
首先安装 Babel:
$ npm install babel-core --save
安装 async/await 支持:
$ npm install babel-preset-stage-3 --save
在服务端代码的根目录中配置 .babelrc 文件,内容为:
{ "presets": ["stage-3"] }
在顶层代码文件(server.js 或 app.js 等)中引入 Babel 模块:
require("babel-core/register");
在这句后面引入的模块,都将会自动经过 babel 编译,但当前文件不会被 babel 编译。另外,须要注意 Node.js 的版本,若是是 4.0 以上的版本则默认支持绝大部分 ES6,能够直接启动。可是若是是 0.12 左右的版本,就须要经过 node —harmory
来启动才可以支持。由于 stage-3 模式,Babel 不会编译基本的 ES6 代码,环境既然支持又何须要编译为 ES5?这样作也是为了提升性能和编译效率。
能够经过增长 Gulp 的预编译 task 来支持。
首先安装 gulp-babel 插件:
$ npm install gulp-babel --save-dev
而后编写配置:
var gulp = require('gulp'); var babel = require('gulp-babel'); gulp.task('babel', function() { return gulp.src('src/app.js') .pipe(babel()) .pipe(gulp.dest('dist')); });
除了 Gulp-babel 插件,也可使用官方的 Babel-loader 结合 Webpack 或 Browserify 使用。
要注意的是,虽然官方也有纯浏览器版本的 Babel.js,可是浏览器限制很是多,并且对客户端性能影响也较大,不推荐使用。
LeanEngine(云引擎)是 LeanCloud 推出的服务器端运行环境,支持 Node.js 和 Python 环境,功能强大并且目前免费,结合 LeanCloud JavaScript SDK,使本来复杂的开发工做变得简单高效。目前也支持 Redis 和海外节点,轻松知足你的业务需求。
LeanCloud 使用自已的服务编写出了不少的应用和 Web 产品。为了方便各位开发者基于 LeanEngine 来开发应用,LeanCloud 整理了目前开发 Web 端产品的技术栈,并结合 LeanEngine 特色,推出了一套完整实用的技术解决方案:LeanEngine-Full-Stack,它已经配置了 Babel,能够在 LeanEngine 中结合 JavaScript SDK 使用 async/await 处理异步操做。因此,还等什么?快来下载编写新项目吧。
Enjoy!
LeanCloud 但愿可以经过构建最简单易用的技术产品,帮助各位开发者和创业者加速产品开发,尽量地节约资源成本、时间成本和机会成本,但愿本文可以帮助到你。有什么问题,能够在 LeanEngine-Full-Stack @GitHub 仓库 中提交 issue,或者直接去 LeanCloud 社区 提问。