You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
其实 promise 方法链更好用的一点是,当下一个操作依赖于上一个操作的结果的时候,可以很方便地通过 then 方法的参数来传递数据。前面页提到过,下一个 then 方法的参数就是上一个 then 方法里面 resolve 的参数,所以当然就可以把上一个 then 方法的执行结果作为参数传递给下一个 then 方法了。
还有些时候,可能 then 方法的执行顺序也没有太多要求,只需要 promise 方法链中的两个或多个 promise 全部都执行正确。这时,如果依旧一个一个去写 then 可能就比较麻烦,比如:
Abstract
本文主要讲的是如何实现 Promise 的链式调用。也就是
promise().then().then().catch()
的形式,然后讨论如何在某一个then()
里面中止 Promise。在程序中,只要返回了一个 promise 对象,如果 promise 对象不是 Rejected 或 Fulfilled 状态,then 方法就会继续调用。利用这个特性,可以处理多个异步逻辑。但有时候某个 then 方法的执行结果可能会决定是否需要执行下一个 then,这个时候就需中止 promise,主要思想就是使用 reject 来中止 promise 的 then 继续执行。
“中止”这个词不知道用得是否准确。这里可能还是
break
的含义更精确,跳出本次 promise,不继续执行后面的 then 方法。但 promise 依旧会继续执行。Can I use promises
当前浏览器对 Promise 的支持情况见下图:
http://caniuse.com/#search=promise
Promise
先简单复习一下 Promise。Promise 其实很简单,就是一个处理异步的方法。一般可以通过
new
方法来调用Promise
的构造器实例化一个 promise 对象:用
new Promise
实例化的 promise 对象有以下三个状态:"has-resolution" - Fulfilled。resolve(成功)时,此时会调用 onFulfilled
"has-rejection" - Rejected。reject(失败)时,此时会调用 onRejected
"unresolved" - Pending。既不是resolve也不是reject的状态,也就是promise对象刚被创建后的初始化状态等
关于上面这三种状态的读法,其中左侧为在 ES6 Promises 规范中定义的术语, 而右侧则是在 Promises/A+ 中描述状态的术语。基本上状态在代码中是不会涉及到的,所以名称也无需太在意。
Promise Chain
先来假设一个业务需求:在系统中使用教务系统账号进行登录。首先用户在登录页面输入用户名(教务系统账号)和密码(教务系统密码);然后判断数据库中是否存在该用户;如果不存在则使用用户名和密码模拟登录教务系统,如果模拟登录成功,则存储用户名和密码,并返回登录成功。
听起来就有点复杂对不对?于是画了个流程图来解释整个业务逻辑:
上图只是一个简化版本,比如密码加密、session设置等没有表现出来,大家知道就好。图中
(1)
、(2)
、(3)
三个地方就是会进行异步处理的地方,一般数据库操作、网络请求都是异步的。如果用传统的回调函数
callback
来处理上面的逻辑,嵌套的层级就会比较深,上面的业务因为有三个异步操作所以有三层回调,代码大概会是下面的样子:上面的代码可能存在的不优雅之处:
接下来再用 promise 实现此处的业务需求。使用 promise 编码之前,可以先思考两个问题。
一是如何链式调用,二是如何中止链式调用。
How to Use Promise Chain
业务中有三个需要异步处理的功能,所以会分别实例化三个 promise 对象,然后对 promise 进行链式调用。那么,如何进行链式调用?
其实也很简单,直接在 promise 的 then 方法里面返回另一个 promise 即可。例如:
上面的代码最终会输出:
代码的执行逻辑如图:
从图中可以看出来,代码的执行逻辑是
promise start --> promise p1 --> promise p3 --> promise p4
。所以结合输出结果和执行逻辑图,总结出以下几点:How to Break Promise Chain
接下来就该讨论如何中止 promise 方法链了。
通过上面的例子,我们可以知道 promise 的状态改变为 rejected 后,promise 就会跳过后面的 then 方法。
也就是,某个 then 里面发生异常后,就会跳过 then 方法,直接执行 catch。
所以,当在构造的 promise 方法链中,如果在某个 then 后面,不需要再执行 then 方法了,就可以把它当作一个异常来处理,返回一个异常信息给 catch,其参数可自定义,比如该异常的参数信息为
{ notRealPromiseException: true}
,然后在 catch 里面判断一下notRealPromiseException
是否为true
,如果为true
,就说明不是程序出现异常,而是在正常逻辑里面中止 then 方法的执行。代码大概就这样:
这样的做法可能不符合
catch
的语义。不过从某种意义上来说,promise 方法链没有继续执行,也可以算是一种“异常”。Refactor Callback with Promise
讲了那么多道理,现在就改来使用 promise 重构之前用回调函数写的异步逻辑了。
在上面的代码中,实例化了三个 promise 对象,分别实现业务需求中的三个功能。然后通过 promise 方法链来调用。相比用回调函数而言,代码结构更加清晰,也更易读易懂耦合度更低更易扩展了。
Promise.all && Promise.race
仔细观察可以发现,在上面的 promise 代码中,
loginEducationSystem
和saveUserToDB
两个方法执行有先后顺序要求,但没有数据传递。其实 promise 方法链更好用的一点是,当下一个操作依赖于上一个操作的结果的时候,可以很方便地通过 then 方法的参数来传递数据。前面页提到过,下一个 then 方法的参数就是上一个 then 方法里面
resolve
的参数,所以当然就可以把上一个 then 方法的执行结果作为参数传递给下一个 then 方法了。还有些时候,可能 then 方法的执行顺序也没有太多要求,只需要 promise 方法链中的两个或多个 promise 全部都执行正确。这时,如果依旧一个一个去写 then 可能就比较麻烦,比如:
现在只需要
p1
p2
p3
这三个 promise 都执行,并且 promise 最终状态都是 Fulfilled,那么如果还是使用方法链,这是这样调用:代码貌似就不那么精炼了。这个时候就有了
Promise.all
这个方法。Promise.all
接收一个 promise对象的数组作为参数,当这个数组里的所有 promise 对象全部变为 resolve 或 reject 状态的时候,它才会去调用then
方法。于是,调用这几个 promise 的代码就可以这样写了:
这样看起来貌似就精炼些了。
而对于
Promise.race
,其参数也跟Promise.all
一样是一个数组。只是数组中的任何一个 promise 对象如果变为 resolve 或者reject 的话,该函数就会返回,并使用这个 promise 对象的值进行 resolve 或者 reject。这里就不举例了。
Conclusion
到目前为止,我们就基本了解了 Promise 的用法及特点,并实现用 Promise 重构用回调函数写的异步操作。现在对 Promise 的使用,应该驾轻就熟了。
完。
Github Issue: #23
The text was updated successfully, but these errors were encountered: