有意图写下以下关于 promise 的几篇文章,目的在于将自己最近对于 promise 的学习归纳、总结出来,同时由于一方面是通过自己学习的一家之言,另一方面也是通过对各种鱼龙混杂疏通和整理加上自己的感悟,发布在社区里面希望各位能够提点一二。
当我们去深入学习、研究一个东西,我们一定会问自己,这个东西有什么用处,有什么好处,能为我们带来什么。
那么为什么用 promise ?下面是我能想到的大部分人的答案。
1 用异步的方式来表达异步的代码是艰难的。我们需要一种以同步的代码来尽可能隐藏具体的异步实现的方式以方便理解,而 promises 能满足这个需求。当使用 promise 编码时候,我们以同步的方式来编码而不需要关心它的实现的同步还是异步。
2 方便编写出更加优美的代码,消除 node 原生的回调函数在代码外观上显示出来的由于不断缩进所形成的金字塔。
......
如果你想到的是上面两点,那么,我在这里,想要更深入一点地谈一下 promise 。
这是最通俗的一个问题。
没错, promise 就是一个状态的容器,是一个异步的、非暂存的、不可变的值。
异步: 这个值可能现在就存在,也可能是将来某个时刻的一个值。
非暂存: 无论 promise 最后的结果是什么(success 或者 failure),他就不会改变了,你总是可以获得这个消息只要你不销毁 promise 。
不可变: 一旦一个 promise 的状态变为 resolved 或 rejected ,就不能改变也不会改变,即完成的 promise 是不可变的。
promise 的可靠性是我们所关注的,这是 promise 的精髓所在, promise 可靠性即为完成的 promise 是不可变的, promise 的这个特性的好处有两点:
1 与原生回调不同, promise 只会运行一次,然后就不用了。
2 可预测性。
当然我首先不否认, promise 可以处理异步回调的嵌套和缩进,但这不是关键。
首先抛出一个问题,回调真正的问题在哪儿?你可以静下来思考一下看你对这个问题是否陌生,而你的理解又是否正确、是否能说服你自己。来看下面一段代码。
// 1 balabalabala
aAsyncFuc(function(){
// 2 heiheieheihei
}) ;
我们需要思考的一个问题是,在代码 1 和代码 2 之间发生了什么?这之间是谁在进行代码的流程控制。
表层上看来,是 aAsyncFuc(..)控制着这些。是你自己创建并管理 aAsyncFuc()吗?许多时候不是,这个 aAsyncFuc 很多时候可能是你所使用的一个第三方库中的一个方法。那么当 aAsyncFuc(..)是一个第三方库中的一个方法时,你信任这个第三方库吗?
你会问,信任什么?信任就是你以为这个 aAsyncFuc(..)会很完美地做到下面这些:
不会太早调用我的回调函数
不会太迟调用我的回调函数
不会少于实际应该调用的次数,比如不会漏掉函数调用
不会多于实际应该调用的次数,比如重复调用
会给我的回调提供必要的参数
在我的回调失败的时候会提醒我
.......
你是如此地信任这个第三方类库中的方法,导致在不知不觉中你把你的代码控制权转交给了这个你实际上并不那么信任的第三方库。
在你的程序的前半部分,你控制着程序的进程。现在你转移了控制权, aAsyncFuc(..)控制了你剩余程序什么时候返回以及是否返回。
像这样一个简单的回调就会把你的控制权转移出去,那么当你编写许多深层次的嵌套回调的时候,可以想象一下控制转移的增长性以及这种转移控制的危害性。
那么,如何解决?
现在 npm 上的那些第三方类库,面向你都是免费的,人家都没跟你要钱,你爱用不用,但有些确实是融合了作者的智慧,使用这些包能让你节省很多劳力和脑力,但是我们也不可能去对每个要用到的第三方包孜孜以求,查源码,测试每一个函数输入输出的正确性......这个时候, promise 机制的卓越性就体现出来了。
promise 改变了传递回调的地方。
在此之前,我先给大家温习一下标准 promise 机制的保证。
如果 promise 被 resolve ,它要不是 success 就是 failure ,不可能同时存在。
一旦 promise 被 resolve ,它就再也不会被 resolve(不会出现重复调用)。
如果 promise 返回了成功的信息,那么你绑定在成功事件上的回调会得到这个消息。
如果发生了错误, promise 会收到一个带有错误信息的错误通知。
无论 promise 最后的结果是什么(success 或者 failure),他就不会改变了,你总是可以获得这个消息只要你不销毁 promise 。
我们回到“ promise 改变了传递回调的地方”这句话,这里所说的“地方”就是 promises 机制,通过传递回调给拥有良好保证和可预测性的中立 Promises 机制,这个时候 promise 机制帮助我们把我们处理任何函数调用的成功或者失败的方式规范成了可预测的形式,特别是如果这个调用实际上的异步的。 从而使得你实质上重新获得了对于后续程序能很稳定并且运行良好的可靠性。与原生的回调函数是将回调传递给你所调用的第三方库中的 API 相比,当你进行深层次异步调用的时候,你的控制转移都是交给了中立的 Promise 机制,而不是将你的代码流程控制权转交给在你没有对其进行源码分析的第三方类库。