一步一步实现一个符合PromiseA+规范的Promise库(1
今天我们来自己手写一个符合PromiseA+规范的Promise库。大家是不是很激动呢??

我们都知道。在现在的前端开发中,Promise这个东西基本上所有的开发中都会用到。
那必然有些萌新就会问了,Promise到底是个什么东西呢。
按照规范来说。Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
通俗来讲。。这个东西就是为了解决我们平常的回调函数,避免回调地狱的一种解决方案。所以说这个东西大家不仅要会用哦,还应该知道他的一些原理。so,我们一起来实现下吧。

接下来我们先看一个简单的Promise。

这个就是es6标准中的Promise。我们可以看到,其实Promise就是一个构造函数。
这个构造函数中只有一个参数。这个参数在Promise/A+规范中被称为executor(执行者..我觉得叫执行器蛮好)。
因为这个执行器是为了执行后面的resolve(决定)和reject(拒绝)方法。呃...其实你可以把resolve看作是成功,把reject看做失败。
当然了我们还可以根据自己定义的规则来进行Promise中resolve和reject的调用,不过这是用法,我们这里就当大家会用了。。

然后我们可以看到,在构造函数的实例p中还有一个then方法。这里我们就要想了,既然是构造函数的实例上哪必然这个函数是挂在到这个构造函数的原型上。
还有很重要的一点就是我们可以想一下,在Promise中这个Promise的当前状态是一个问题。在A+规范中规定:一个Promise只有三种状态,我们看图

什么意思呢。。
大致的意思就是说,一个Promise有且仅有(pending->等待,fulfilled->已执行,rejected->已拒绝)这三种状态中的一种(ps:我曾经看到过一个词->悬而未决用来形容pending也不错 :)。
我们知道了这些,接下来我们就来手动实现一个简单的。

继续。。。。

这里我们要说一下,貌似刚才忘说了。。。

我们通过这张图来看一下,当目前的状态为pending时。我们可以将pending状态改变为fulfilled或者rejected中的一种。然而我们要记住,之前已经提到过Promise的状态必须是三种之一。而且,如果一旦成功就不能失败,一旦失败就不能成功。
接下来我们依据上面状态的描述来继续、、、

这里当我们调用resolve和reject的时候我们需要做出状态判断,只有是pending状态的时候才可以改变状态为其他两种的任意一种,如果不是:例如

我们处理完了resolve和reject内置的逻辑,这里有一个问题。在开发中,当Promise的执行遇到错误时,会直接变成rejected状态,大家应该都知道,也就是下面的处理。

我们在Promise的执行过程中如果捕获到异常,就可以直接调用reject来结束Promise。
接下来我们看then方法。

这样我们就实现了一个简单的Promise(才没有。。。这才哪到哪),我们来试下效果吧。

别忘了导出我们的Promise。。



我们看到下面的输出结果,哇!!好激动有没有(才没有激动),我也实现了一个Promise!
但是!有一个问题,我们是不是弄丢了一个状态???
what??哪个??仔细想一下好像是‘pending’丢掉了。。
so?那咋办。
有人可能会说了,不是出了成功就是失败吗,为什么会有等待状态呢,我们来思考一下下面的代码。

话说Promise应该都是支持异步的吧?就像上面的代码,异步执行resolve的时候我们是不是已经吧值给弄丢了?
并且我们想一下,弄丢这个值得一段时间是不是就是等待态也就是‘pending’的时候。。
所以,我们要怎么处理这个pending呢?
我们想一下,在executor中的resolve和reject是不是都会吧我们传入的值,传到then方法的onfulfilled和onrejected中?另外,我们在then方法中做了对目前Promise的状态的判断。
所以我们在then方法中去处理‘pending’状态。
怎么去处理呢?我们可以在Promise中挂载两个数组。

为什么?这两个数组的作用是为了记录pending状态下的onfulfilled和onreject函数。我们来看then中的代码。

我们可以看到在pending状态下,这两个数组分别记录了各自对应的then的回调函数,并且保存起来。
我们来捋一捋思路。

所以说我们的数组里存的是一个一个的then的回调函数,也就是一个一个function。
所以我们要在resolve和reject方法触发的时候,去便利我们的数组并且执行其中的方法,并且呢还要把我们成功的原因(self.value)和失败的原因(self.reason)放到我们的回调方法中去。说了这么多有点绕。。上代码

这样就解决了异步的问题。我们再来测试一下。
代码刚开始运行。

2秒后。。。

这时我们就拿到了异步的值,是不是很开心!(有点,嘿嘿)
再看一下我们写的全部的代码:

到这里我们简单了解了Promise的一小部分原理,并且实现了一个非常简单的Promise。今天就先写到这里,在下一章中我们会继续了解Promise中的then方法是如何链式调用的,以及链式调用中的许多坑。。。
好啦,谢谢大家看到这里。感谢。

更新。下一篇文章地址为:一步一步实现一个符合PromiseA+规范的Promise库(2)
再次感谢。