Javascript语言的执行环境是”单线程”(single thread)。为了解决由于执行耗时任务导致整个页面的卡顿的问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
1 | // 可能会涉及到跨域请求的问题,tornado 解决方案: |
async/await
虽然 co 是社区里面的优秀异步解决方案,但是并不是语言标准,只是一个过渡方案。ES7语言层面提供 async/await 去解决语言层面的难题。
1 | const request = require('request'); |
tips:
- async 用来申明里面包裹的内容可以进行同步的方式执行,await则是进行执行顺序控制,每次执行一个 await,程序都会暂停等待 await 返回值,然后再执行之后的 await。
- await 后面调用的函数需要返回一个 promise,另外这个函数是一个普通的函数即可,而不是 generator。
- await 只能用在 async 函数之中,用在普通函数中会报错。
- await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
其实,async/await 的用法和 co 差不多,await 和 yield 都是表示暂停,外面包裹一层 async 或者 co 来表示里面的代码可以采用同步的方式进行处理。不过 async/await 里面的await 后面跟着的函数不需要额外处理,co 是需要将它写成一个 generator 的。
Promise
使用 Promise 可以很好的减少嵌套的层数,Promise 的实现采用了状态机,在函数里面可以很好的通过 resolve 和 reject 进行流程控制,可以按照顺序链式的去执行一系列代码逻辑。下面是使用 Promise 的一个例子:
1 | const request = require('request'); |
不过 Promise 仍然存在缺陷,它只是减少了嵌套,并不能完全消除嵌套。举个例子,对于多个 promise 串行执行的情况,第一个 promise 的逻辑执行完之后,我们需要在它的 then 函数里面去执行第二个 promise,这个时候会产生一层嵌套。
Generator
在 Node.js 中经常用的 tj/co 就是使用 generator 结合 promise 来实现的,co 是 coroutine 的简称,借鉴于 python、lua 等语言中的协程。它可以将异步的代码逻辑写成同步的方式,这使得代码的阅读和组织变得更加清晰,也便于调试。
1 | const co = require('co'); |
回调函数
这是异步编程最基本的方法。假定有两个函数fa和fb,后者等待前者的执行结果。如果fa是一个很耗时的任务,可以考虑改写fa,把fb写成fa的回调函数:
1 | // 改写前 |
回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。
1 | function a() { |
执行结果:
1 | oneSecond |