回调地狱
最早的时候,如果我们要写一些node程序,就会调用大量的异步接口,最终形成层层嵌套的代码结构。而这种结构会导致代码的可读性大大降低。代码几乎难以理解,维护这样的代码是一件很麻烦的事情。
|
|
Promise
链式结构
为了解决回调地狱带来的问题,出现了Promise的方案。Promise的目标是将代码的嵌套结构变成链式结构。下面的代码就是链式结构的,Promise并不是这样的但是类似。
|
|
Promise对象
每一个Promise对象都代表一系列操作,这些操作可以是同步的或者异步的,Promise对象并不关心。重要的是,当这一系列操作完成之后需要将结果告知Promise对象。
Promise对象在构造时接受一个函数(表示一系列操作)并立即调用它。在调用该函数时,Promise对象会给其传递两个函数,依次是resolve
和reject
。当一系列操作完成后,就能够通过这两个函数将结果告知Promise对象。如果一切正常则调用resolve(value)
,如果发生错误则调用reject(error)
,这里的value
和error
是一系列操作的结果。
|
|
状态
Promise对象拥有状态,刚刚创建的时候为pending
,resolve函数调用后变为fulfilled
,reject函数调用后变为rejected
。状态一旦变成fulfilled
或者rejected
后就无法改变了。
|
|
then方法
在创建完一个Promise对象后,可以通过then
方法来指定回调函数,then
方法接受两个回调函数onValue
和onError
,其中onError
可选。Promise对象会依据自身状态在正确的时机调用对应的回调函数。如果状态是pending
则保存回调函数,等到Promise的状态改变后再调用这些保存的函数。如果状态不是pending
则立即调用回调函数(异步立即执行)。
|
|
then的链式调用
现在来看,Promise比起最初的回调形式并没有太多的改变,仅仅只是把作为参数的回调函数从异步方法拿到了then方法中。多个异步操作组合起来就会像是下面这样,其中getPost
、renderView
等方法现在返回一个Promise对象。
|
|
这显然没有任何改变,不要忘了Promise的目标是链式调用。为了达到链式调用的目的,then方法返回的其实是一个新的Promise对象,该对象代表了then方法中的回调函数的执行。新的Promise对象的状态取决于回调函数的执行结果,有三种情况:
- 回调函数抛出异常error,直接
reject(error)
。 - 回调函数返回了一个非Promise对象的值value,直接
resolve(value)
。 - 回调函数返回了一个Promise对象,那么该对象的状态决定了then方法返回的Promise对象的状态。
所以,我们的代码就可以写成下面这样:
|
|
整理一下代码就变成:
|
|
是不是一下就清爽多了?我写的一个Promise的 简陋实现。
Generator
Generator介绍
generator的本质是一个可控的分段执行的函数,generator由generator函数生成。定义函数时在function
后加一个*
就可以将其声明为generator函数了,函数执行后返回一个generator对象。调用generator的next
方法可以让代码执行,直到遇见yield
或者return
为止。next
方法返回的是一个对象,结构类似这样:{value:1,done:false}
,其中value是yield
或者return
后的值,如果遇到的是yield
则done的值为false
,如果遇到的是return
则为true
。
|
|
next方法接受一个参数,这个参数会取代yield表达式的值。
|
|
结合Promise使用
如果我们利用generator分段执行的特性,就能够把异步代码写得像同步的一样。每当调用异步方法时,就暂停函数的执行,待其完成后再继续执行。
|
|
当yield出的Promise对象的状态发生变化时就调用next方法让函数继续执行,Promise的结果正好可以作为next方法的参数传递给generator。为了让generator运行起来,还需要一些额外的代码。
|
|
很丑陋,好在有第三方的库能够帮我们自动运行这些generator,这个库就是 co,我自己也实现了一个 简陋的版本。
async和await
Generator和Promise结合使用的办法被最新的JavaScript标准采纳了,叫做async
和await
。所有的内容和上面都是一致的,仅仅只是把yield
换成await
、function*
换成async function
。
|
|
最终,异步代码写起来和同步代码几乎一摸一样了。大家过上了幸福的生活。