吼吼哈嘿 發表於 2022-7-19 13:49:00

Javascript之我也来手写一下Promise

<p>  Promise太重要了,可以说是改变了JavaScript开发体验重要内容之一。而Promise也可以说是现代Javascript中极为重要的核心概念,所以理解Promise/A+规范,理解Promise的实现,手写Promise就显得格外重要。如果要聊Promise就要从回调函数聊到回调地狱,再聊到同步异步,最终聊到Promise、async await。但是我们这篇文章,目的是手写Promise,这些前置知识如果大家不了解的话,希望可以去补充一下。那你可能会说了,我他妈不懂你在说啥,我就是想手写Promise,不行么?大佬~~那肯定是没问题的。好了,废话不多说,咱们开始吧。</p>
<h2>一、实现Promise的基本结构</h2>
<p>  最开始的部分,我们不得不去看看Promise/A+规范,因为我们所实现的、手写的代码,其实都是根据规范,一步一步来实现的。规范的地址,附在文末。</p>
<p>  整个Promise/A+规范有三个部分:术语、要求、说明。术语部分介绍了核心字段的关键性解释,要求部分基本上就是对整个Promise实现的要求,说明部分,则对Promise的实现提供了一些特殊场景的补充说明。</p>
<p>  首先,我们需要简单了解下Promise的基本概念。Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了<code>Promise</code>对象。与Promise交互的主要方式是通过它的then方法,该方法会注册一个回调函数,用来接收Promise的最终结果,这个结果可能是Promise的最终值,也可能是一个失败的原因。也就是Promise的resolve和reject呗~</p>
<p>  Promise/A+规范详细说明了<strong>该then方法的行为</strong>,所有符合Promise/A+规范所实现的Promise都可以基于此来提供一个可以互相操作的基础。因此,该规范应该是十分稳定的。也就是说,该Promise/A+规范并不会随意迭代,提供了一个长期稳定不变的规范版本。当然,也可能会有小的修改,但是一定是经过深思熟虑的。额~这些都是废话。重点就是最开始那一句:<strong>详细说明了then方法的行为。</strong>记住这句话!记住这句话!记住这句话!因为我们后面所做的一切,除了基本结构和参数,<strong>所有的一切都是围绕着then展开的。</strong></p>
<p>  最后,核心的Promise/A+规范并不会去管你怎么实现,<span><span>而是选择专注于提供可互操作的</span></span><code class="highlighter-rouge">then</code><span><span>方法。换句话说,我不管你写怎么实现Promise,我只关注你应该怎么实现then方法的可操作性。</span></span></p>
<p><span><span>  好了,基本的背景,我们了解了,我们之前说了,整个Promise/A+规范有三部分,我们先来看看第一部分术语,并实现这第一部分。</span></span></p>
<ol>
<li>“promise”是一个function或者object,它的行为要符合本规范。</li>
<li><span><span>“thenable”是一个function或者object,它定义了then方法。(啥意思呢,其实就是:thenable是一个可以调用then方法的function或者object,再换句话说,就是我们new Promise().then的这个new Promise()呗,你咋实现我不管,有就行。)</span></span><span><span><br></span></span></li>
<li><span><span>“value”是任何合法的值(包括thenable、promise或者undefined)。</span></span></li>
<li><span><span>“exception”是使用throw语句抛出的值。</span></span></li>
<li><span><span>“reason”是一个用来表示Promise为什么被reject的原因,的值。</span></span></li>












</ol>
<p><span><span>  就这些,但是我稍微解释下,promise是指Promise这个整体,它可以是一个构造函数,也可以是个对象。如果Promise是个构造函数的话,那么thenable,其实就是Promise这个构造函数的实例,这个实例要有一个then方法。继续,exception就是异常处理,通过try catch语句来抛出错误。那么最后,value和reason分别对应了resolve和reject的参数。</span></span></p>
<p><span><span>  巴巴了这么多,我们先来写一点代码吧,不然长篇大论确实有点枯燥。</span></span></p>
<p><span><span>  前面总结了一下Promise基本背景,还有基本的术语,我们这一小节就来实现基于此的代码,但是我先总结一下哈,我们要实现的到底包括哪些内容。</span></span></p>
<p><span><span>  首先,我们要实现一个Promise类,这个类会提供一个实例方法也就是then方法。然后,我们还要有两个值:value和reason。最后,我们还要处理抛出的异常。没啦~一句话概括,精炼!</span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">class Promise {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">constructor(excutor) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.reason =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   const resolve = (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   const reject = (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      excutor(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      reject(err);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">then(</span>onFulfilled<span>, onRejected) {}</span></pre>
<pre><span style="color: rgba(0, 128, 128, 1)">14</span> }</pre>
</div>
<p>  简单不,其实啥也没有,就是按照之前我们描述的规范,来一步一步实现的。我简单再总结下,首先我们声明了一个Promise类,然后这个类有一个实例方法then,这个then方法接收两个回调函数(后面我们会完善的),然后整个类的构造函数部分,声明了一个value和reason,分别就是该Promise构造函数对应结果状态的值。最后,我们通过try…catch语句执行传递进来的回调excutor,并把resolve和reject方法作为excutor的回调。这里稍微有点绕,要捋一下噢。</p>
<p>  那么,我们再根据使用时的场景,来巩固一下上面的代码,通常,我们在使用Promise的时候,是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> const p1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt; {});</pre>
</div>
<p>  我们看,使用的时候,我们会给Promise这个类传过去一个方法,OK,这个方法就是我们Promise类中excutor。而这个executor又传回去了两个函数参数,可以让我们在new Promise时传递的executor的内部去调用:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> const p1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   resolve("success"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   reject("fail"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">7</span> });</pre>
</div>
<p>  这样我们的value和reason就传递给了constructor中的resolve和reject。最后,还有个实例方法then:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">p1.then(
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   console.log("成功"<span style="color: rgba(0, 0, 0, 1)">, value);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">6</span>   console.log("失败"<span style="color: rgba(0, 0, 0, 1)">, reason);
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">8</span> );</pre>
</div>
<p>  这个then方法有两个参数,分别是成功的回调函数和失败的回调函数,也就是分别代表了Promise类中的实例的then方法的onFulfilled, onRejected。那么基本的结构我们就写完了,但是现在我们写的这个Promise肯定是没法用的。所以,我们还得继续往下,看看规范怎么要求的,我们按照要求再来完善。</p>
<h2>二、完成基本的Promise</h2>
<p>  我们前面完成了基本的结构,但是那些代码还缺了一些内容,这一小节,我们就来根据后面的Promise/A+规范,也就是要求部分,来实现、完善后面的代码。首先,规范就对Promise的状态,进行了详细的描述。</p>
<p>  promise必须处于以下三种状态之一:pending, fulfilled, or rejected。也就是待处理、已完成或已拒绝。</p>
<p>  &nbsp; 1.&nbsp;当处于pending状态时:</p>
<ul>
<li style="list-style-type: none">
<ul>
<li>可以转换到已完成(fulfilled)或者已拒绝(rejected)状态。</li>
</ul>
</li>
</ul>
<p><span><span>  &nbsp; 2. 当处于fulfilled状态时:</span></span></p>
<ul>
<li style="list-style-type: none">
<ul>
<li><span><span>不可以改变状态。</span></span></li>
<li><span><span>必须有一个不能改变的value。</span></span></li>
</ul>
</li>
</ul>
<p><span><span>  &nbsp; 3.&nbsp; 当处于rejected状态时:<br></span></span></p>
<ul>
<li style="list-style-type: none">
<ul>
<li><span><span>不可以改变状态。</span></span></li>
<li><span><span>必须有一个不能改变的reason。</span></span></li>












</ul>












</li>












</ul>
<p><span><span>  OK,这就是关于Promise的状态的描述。<strong>其实上面的说明很好理解,就是我们要给Promise这个类维护一个status字段,这个status有三个状态常量。一旦确定了status的结果,也就是status如果不是pending的话,就不能再修改status的状态了。</strong><br></span></span></p>
<p><span><span><strong>  </strong>我们继续,针对then方法的详细描述,当然,我们这个阶段,只需要其中的一部分。<strong>首先,Promise必须提供一个then方法来让使用者可以访问当前或最终状态的value或reason。其次,Promise的then方法接受两个参数:</strong></span></span></p>
<div class="cnblogs_code">
<pre>promise.then(onFulfilled, onRejected)</pre>
</div>
<p>  诶嘿!就是我们上面写的噢!继续~注意,以下的列表标记,完全抄袭自Promise/A+规范,方便大家查找。</p>
<p>  2.2.1 onFulfilled和onRejected都是可选参数</p>
<p>    2.2.1.1&nbsp; 如果onFulFilled不是函数,则忽略。</p>
<p>    2.2.1.2&nbsp; 如果onRejected不是函数,则忽略。</p>
<p>  2.2.2&nbsp; 如果onFulfilled是一个函数</p>
<p>    2.2.2.1&nbsp; onFulfilled必须在promise已经是fulfilled的状态后调用,并且把promise的value作为它的第一个参数。</p>
<p>    2.2.2.2&nbsp; 在promise已经是fulfilled状态之前该方法不能被调用。</p>
<p>    2.2.2.3&nbsp; 该方法只能被调用一次。</p>
<p>  2.2.3&nbsp; 如果onRejected是一个函数</p>
<p>    2.2.3.1&nbsp; onRejected必须在promise已经是rejected的状态后调用,并且把promise的reason作为它的第一个参数。</p>
<p>    2.2.3.2&nbsp; 在promise已经是rejected状态之前该方法不能被调用。</p>
<p>    2.2.3.3&nbsp; 该方法只能被调用一次。</p>
<p>  2.2.4&nbsp; onFulfilled或者onRejected必须在执行上下文栈只有平台代码的时候才可以被调用。</p>
<p>  2.2.5&nbsp;&nbsp;onFulfilled或者onRejected必须作为函数被调用(即没有this值)。</p>
<p>  OK,这一段规范翻译,其中尤其要关注的是2.2.4和2.2.5。</p>
<p>  首先,2.2.4是什么意思呢?平台代码,就是指环境、引擎、或者promise实现的代码,也就是说在onFulfilled或者onRejected被调用的时候,只能是在执行上下文栈中的最底层,它的前面要么是全局、要么是另一个符合规范的promise。在实践中,这样可以确保在调用事件环之后异步执行onFulfilled或者onRejected回调。这可以通过宏任务(比如&nbsp;setTimeout&nbsp;或者setImmediate)或微任务(MutationObserver或者process.nextTick)机制来实现。由于promise的实现会被当作平台代码,所以它本身可能包含一个任务调度队列或者一个调用处理程序的“蹦床”。这里的蹦床可以理解成一种转换函数,比如递归可能会导致栈溢出,那么可以通过蹦床函数把递归转换成循环,绕过JS的栈溢出检测机制。当然你也可能会写成死循环,那就是你代码的问题了。</p>
<p>  其次,2.2.5比较容易理解,就是指你实现的onFulfilled或者onRejected只能是一个函数,并且它内部的this如果在严格模式下是undefined,在非严格模式,则是全局对象。换句话说,也就是你的onFulfilled或者onRejected不会被任何对象或者函数调用,不归属于任何对象或者函数。</p>
<p>  理解了这段规范了吧?那么我们开始手写代码吧。</p>
<p>  首先,根据规范,我们要加入三个状态常量,并且Promise的初始状态肯定是pending:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const PEDNING = "PENDING"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> const FULFILLED = "FULFILLED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> const REJECTED = "REJECTED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">class Promise {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">constructor(excutor) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> PENDING;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.reason =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   const resolve = (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   const reject = (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">      excutor(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">      reject(err);
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {}
</span><span style="color: rgba(0, 128, 128, 1)">18</span> }</pre>
</div>
<p>  额外的,我们还要维护一个队列,这个队列用来当我们调用resolve或者reject的时候,触发我们传给then方法的函数参数。我们还要完善resolve和reject方法,以及then方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const PENDING = "PEDNING"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> const FULFILLED = "FULFILLED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> const REJECTED = "REJECTED"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">class Promise {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">constructor(excutor) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> PENDING;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.reason =<span style="color: rgba(0, 0, 0, 1)"> undefined;
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.onResolvedCallbacks =<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.onRejectedCallbacks =<span style="color: rgba(0, 0, 0, 1)"> [];
</span></strong><span style="color: rgba(0, 128, 128, 1)">11</span>   const resolve = (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       <strong><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> value;
</span><span style="color: rgba(0, 128, 128, 1)">14</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> FULFILLED;
</span><span style="color: rgba(0, 128, 128, 1)">15</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.onResolvedCallbacks.forEach((cb) =&gt; cb(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value));
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span></strong><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">    };
</span><span style="color: rgba(0, 128, 128, 1)">18</span>   const reject = (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)">19</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">20</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.reason =<span style="color: rgba(0, 0, 0, 1)"> reason;
</span><span style="color: rgba(0, 128, 128, 1)">21</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> REJECTED;
</span><span style="color: rgba(0, 128, 128, 1)">22</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.onRejectedCallbacks.forEach((cb) =&gt; cb(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason));
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span></strong><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">    };
</span><span style="color: rgba(0, 128, 128, 1)">25</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">      excutor(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">27</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">      reject(err);
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">32</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
</span><span style="color: rgba(0, 128, 128, 1)">33</span>       onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">35</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> REJECTED) {
</span><span style="color: rgba(0, 128, 128, 1)">36</span>       onRejected(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason);
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">38</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">39</span>       <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onResolvedCallbacks.push(onFulfilled);
</span><span style="color: rgba(0, 128, 128, 1)">40</span>       <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onRejectedCallbacks.push(onRejected);
</span><span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span></strong><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">43</span> }</pre>
</div>
<p>  我们来看下上面的代码,第9、10行,我们分别维护了一个数组,当然我们在promise仍旧是pending状态的时候调用then方法,就会把这两个状态的回调函数缓存起来。等到我们调用resolve或者reject确认了promise的状态时,就会去调用对应的缓存队列执行回调。</p>
<p>  <strong>resolve和reject方法十分简单,绑定value或者reason,改变状态,然后执行对应缓存的回调函数。而then方法的处理目前也并不复杂,根据状态去调用对应回调或者缓存回调。</strong></p>
<p><strong>  </strong>注意,之前我们翻译规范的时候还说过<strong>要确保在调用事件环之后异步执行onFulfilled或者onRejected回调。</strong>这个我们后面再实现,所以现在还都是同步代码。我们来测试一下我们的代码执行效果吧:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const p1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   resolve("success"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">p1.then(
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   console.log("成功"<span style="color: rgba(0, 0, 0, 1)">, value);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   console.log("失败"<span style="color: rgba(0, 0, 0, 1)">, reason);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> const p2 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   reject("fail"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">17</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">p2.then(
</span><span style="color: rgba(0, 128, 128, 1)">19</span>   (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">20</span>   console.log("成功"<span style="color: rgba(0, 0, 0, 1)">, value);
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">22</span>   (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">23</span>   console.log("失败"<span style="color: rgba(0, 0, 0, 1)">, reason);
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">25</span> );</pre>
</div>
<p>  我们来分析下这段代码是如何执行的。首先,当我们new Promise的时候就会执行Promise这个类的constructor,此时生成了部分实例字段和resolve、reject方法。并且执行了excutor方法,那么由于我们在方法内部调用resolve方法,也就相当于constructor是这样执行的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> excutor(resolve,reject){
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   resolve("success"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">excutor(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">6</span> } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (err) {
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">reject(err);
</span><span style="color: rgba(0, 128, 128, 1)">8</span> }</pre>
</div>
<p>  那么此时,我们已经先走了resolve方法,也就是先确定了promised状态,再强调一下,现在都是同步的噢,然后,当我们后面再去调用then方法的时候,promise的状态已经是确定的了,所以不会去走缓存,直接走了</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
onFulfilled(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
}</span></pre>
</div>
<p>  这段代码,于是就执行了传给then的onFulfilled状态的回调。p2的例子也是一样的。</p>
<p>  那么我们继续再来看个例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const p1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   resolve(1<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">p1.then(
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">    console.log(value);
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    console.log(reason);
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">14</span> );</pre>
</div>
<p>  这个例子的执行结果是什么?我先不说答案,来捋一下,首先我们执行了excutor,但是此时我们并没有调用resolve,因为是异步的,所以要在一秒后才去调用resolve,那么此时executor就执行完了,然后我们去执行then方法,此时状态还没确定,所以走then的时候就走了这段代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onResolvedCallbacks.push(onFulfilled);
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onRejectedCallbacks.push(onRejected);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> }</pre>
</div>
<p>  缓存了起来,那么等到一秒后,调用了resolve,由于此时还是pending状态,所以resolve就走了其内部的逻辑:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> const resolve = (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> value;
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> FULFILLED;
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   <span style="color: rgba(0, 0, 255, 1)">this</span>.onResolvedCallbacks.forEach((cb) =&gt; cb(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value));
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">7</span> };</pre>
</div>
<p>  所以一秒后才会打印出1。</p>
<h2>三、完善Promise的then方法</h2>
<p>  那么继续,我们要进入下一个阶段了,之前的阶段我们解读(就是把规范复制过来翻译翻译)的规范还欠了点债,就是<strong>异步处理。</strong>那么这一小节,我们继续翻译剩下的规范,并且把欠大家的债还上。</p>
<p>  2.2.6&nbsp; then方法可以被同一个promise调用多次</p>
<p>    2.2.6.1&nbsp; 当promise是fulfilled状态的时候,所有相关的onFulfilled回调函数必须按照then方法调用的顺序执行。</p>
<p>    2.2.6.2&nbsp;&nbsp;当promise是rejected状态的时候,所有相关的onRejected回调函数必须按照then方法调用的顺序执行。</p>
<p>  2.2.7&nbsp; then方法必须返回一个promise</p>
<div class="cnblogs_code">
<pre>promise2 = promise1.then(onFulfilled, onRejected);</pre>
</div>
<p>    2.2.7.1&nbsp; 如果<code class="highlighter-rouge">onFulfilled或者</code><code class="highlighter-rouge">onRejected的其中一个返回了一个值,x。那么要运行:<em>Promise Resolution Procedure&nbsp;<code class="highlighter-rouge">[](promise2, x)。</code></em></code></p>
<p><em>    </em>2.2.7.2&nbsp;&nbsp;如果<code class="highlighter-rouge">onFulfilled或者</code><code class="highlighter-rouge">onRejected其中一个抛出了一个错误,e。promise2也必须变成使用e作为reason的拒绝状态。</code></p>
<p>    2.2.7.3&nbsp; 如果onFulfilled不是一个函数,并且promise1已经是fulfilled状态,promise2也必须变成fulfilled状态并且使用和promise1一样的value作为值。</p>
<p>    2.2.7.4&nbsp;&nbsp;如果onRejected不是一个函数,并且promise1已经是rejected状态,promise2也必须变成rejected状态并且使用和promise1一样的reason作为错误信息。</p>
<p>  OK,这就是我们这个阶段要实现的规范。其中有些内容还是要解释下的。首先就是让人疑惑的<em><strong>Promise Resolution Procedure&nbsp;</strong><code class="highlighter-rouge"><strong>[](promise2, x)</strong>,</code></em><code class="highlighter-rouge">这个东西我特意没有翻译,因为你需要把它看作一个整体,现在这个阶段,你可以把它理解成一段要处理<strong>特定逻辑的代码块</strong>。</code></p>
<p>  那么然后就是2.2.7.2到2.2.7.4,实际上只说了一句话,<strong>then方法必须返回一个promise,并且一旦该promise的状态已经确定,后续的promise也一定是同样的状态不得更改,且要把源promise的value或reason依次向后传递。</strong>也就是所谓的值穿透,听起来高大上,实际上没啥复杂的概念。</p>
<p>  我们接下来写代码吧。哦对,2.2.6其实我们上一小节已经写完了,就是那个数组,每次调用then如果promise是pending状态,就会往两个数组中依照then调用的顺序依次往里添加回调。所以,我们这一小节,实际上需要处理的核心内容,就是then方法,或者,我可以在这里确切的告诉大家,<strong>Promise的核心就是这个then方法,Promise中核心的核心是resolvePromise方法,接下来你就知道我为什么这么说了。</strong></p>
<p><strong>  constructor的代码暂时没有变化,这里就不再复制了,我们仅特别重要的关注then的变化,那么第一件要做的就是then方法会返回一个promise:</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let p = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span></strong><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>       onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> REJECTED) {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       onRejected(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onResolvedCallbacks.push(onFulfilled);
</span><span style="color: rgba(0, 128, 128, 1)">11</span>       <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onRejectedCallbacks.push(onRejected);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><strong><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p;
</span></strong><span style="color: rgba(0, 128, 128, 1)">15</span> }</pre>
</div>
<p>  啥也没干哈,就是返回了个新的promise,其实这也就是promise的<strong>链式调用。</strong>那问题来了,为啥我要把then的内容用一个新的promise包裹起来呢?既然要返回个promise,那我直接在代码的最后面,比如这样:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> REJECTED) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   onRejected(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onResolvedCallbacks.push(onFulfilled);
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onRejectedCallbacks.push(onRejected);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><strong><span style="color: rgba(0, 128, 128, 1)">12</span>   let p = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p;
</span></strong><span style="color: rgba(0, 128, 128, 1)">15</span> }</pre>
</div>
<p>  那我这样不就可以了么?别忘了,我们还要把<strong>promise1的结果,传递给promise2,所以我们通过再生成一个新的promise2的内部去执行我们的回调。</strong></p>
<p><strong>  </strong>那么我们要如何把promise1的结果传给下一层呢?我们回头看下2.2.7.1,结果叫做x:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let p = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 4</span>       setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>         let x = onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">          resolvePromise(p, x, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">          reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">11</span>       }, 0<span style="color: rgba(0, 0, 0, 1)">);
</span></strong><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> REJECTED) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">14</span>       setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>         let x = onRejected(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason);
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">          resolvePromise(p, x, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">18</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">          reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">21</span>       }, 0<span style="color: rgba(0, 0, 0, 1)">);
</span></strong><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">23</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)">24</span>       <span style="color: rgba(0, 0, 255, 1)">this</span>.onResolvedCallbacks.push(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)">25</span>         setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">27</span>             let x = onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">            resolvePromise(p, x, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">29</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">            reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">          }
</span><span style="color: rgba(0, 128, 128, 1)">32</span>         }, 0<span style="color: rgba(0, 0, 0, 1)">);
</span></strong><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">      });
</span><span style="color: rgba(0, 128, 128, 1)">34</span>       <span style="color: rgba(0, 0, 255, 1)">this</span>.onRejectedCallbacks.push(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)">35</span>         setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">36</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">37</span>             let x = onRejected(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reason);
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">            resolvePromise(p, x, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">39</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 0, 0, 1)">            reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">          }
</span><span style="color: rgba(0, 128, 128, 1)">42</span>         }, 0<span style="color: rgba(0, 0, 0, 1)">);
</span></strong><span style="color: rgba(0, 128, 128, 1)">43</span> <span style="color: rgba(0, 0, 0, 1)">      });
</span><span style="color: rgba(0, 128, 128, 1)">44</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">46</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p;
</span><span style="color: rgba(0, 128, 128, 1)">47</span> }</pre>
</div>
<p>  OK,这就是这一小节完整的then方法的部分了,甚至是整个promise实现的then方法也基本上跟这个差不多了,我们先来看当调用then方法的时候已经是fulfilled状态了的话,我们会获取到我们传给promise1的<strong>onFulfilled回调的结果作为x,</strong>这就是我们之前规范里所说的x,然后它走了一个resolvePromise,传了四个参数,并且把我们外面的那个p传了进去,那么假设,我没写外面的setTimeout,我能把p传给resolvePromise方法么?<strong>不能!!!</strong>因为我们想要在p还未生成之前就传给了内部的resolvePromise方法,这时候还没有p呢,所以我们利用事件循环机制,包了一层setTimeout,这样等到下一个tik整个p生成,我们就可以传给resolvePromise了。</p>
<p>  那么我们注意,无论在哪一个状态中,两种结果状态也就是fulfilled或rejected时,只要拿到了结果,就会走resolvePromise方法,只有try……catch报错了,才会直接抛出异常。那么这里的resolvePromise,其实就是我们前面没有翻译的那一段逻辑:<em>Promise Resolution Procedure&nbsp;<code class="highlighter-rouge">[](promise2, x)</code></em><code class="highlighter-rouge">,就这个。换句话说promise1的任何确定的状态结果,对于promise2来说都是要去resolve的,除非执行promise2的时候报错了。</code></p>
<p>  简单总结下:</p>
<ul>
<li>then方法要返回个promise2。</li>
<li>then方法的成功或失败的回调函数的结果都是x。</li>
<li>x会传给<strong>resolvePromise做后续的处理。</strong></li>
<li>通过setTimeout的异步来使得resolvePromise可以获取到then方法内新生成的promise2。</li>
<li>无论是失败还是成功,结果都是x,都会传给resolvePromise去做处理,同样的try……catch的报错会走promise1的reject。</li>
</ul>
<h2>四、实现完整的Promise</h2>
<p>  那么这一小节,我们要实现完整的Promise,其实就是完善resolvePromise方法啦。在写代码之前,我们还是要来解读一下规范,我们就是照着规范写代码啦。</p>
<p>  <em>P<strong>romise Resolution Procedure</strong>是一个会接收promise和x作为参数的一个抽象操作,我们把它标记为:[](promise, x)。如果x的表现形式是thenable的,也就是说如果x的结果是一个promise的话,那么假设x的行为与promise相似,则会试图使promise采用x的状态。</em></p>
<p><em>  这种thenable的处理允许promise之间的互相操作,只要这些promise的实现符合本规范的要求。它同样允许符合本规范的实现使用合理的then方法“同化”不符合的实现。</em></p>
<p><em>  <em>[](promise, x)程序需要遵循以下步骤:</em></em></p>
<p><em><em>  </em></em>2.3.1&nbsp; 如果promise和x引用自同一个对象,那么则抛出一个TypeError作为reason的reject状态。</p>
<p>  2.3.2&nbsp; 如果x是一个promise,则采用它的状态。通常,只有当x的实现是符合本规范的要求时,才会知道它是不是一个真正的承诺。本条规则允许使用特定于实现的方法来采用已知符合promise的状态。</p>
<p>    2.3.2.1&nbsp; 如果x是pending的状态,那么promise也必须保持pending状态,直到x的状态已变更为fulfilled或者rejected。</p>
<p>    2.3.2.2&nbsp; 如果x是fulfilled状态,那么就把promise的状态变更为fulfilled并使用与x一样的value。</p>
<p>    2.3.2.3&nbsp; 如果x是rejected状态,那么就把promise的状态变更为rejected并使用与x一样的reason。</p>
<p>  2.3.3&nbsp; 如果x是一个对象或者函数</p>
<p>    2.3.3.1&nbsp; 声明一个then变量存储x.then方法。这样做,其实就是为了缓存一下x.then的引用,那么当我们后续测试该引用,调用该引用的时候,都避免了多次调用x.then的访问。这些预防措施对于确保访问器属性的一致性非常重要,因为访问器属性的值可能在两次检索之间发生变化。</p>
<p>    2.3.3.2&nbsp; 如果我们在执行x.then后抛出了一个异常e,那么promise状态应修改为以e作为参数的rejected状态。</p>
<p>    2.3.3.3&nbsp; 如果then是一个函数,那么则调用then.call,把x作为call的第一个参数(也就是作为then的this),resolvePromise作为第二个参数,rejectPromise作为第三个参数,其中:</p>
<p>      2.3.3.3.1&nbsp; 如果resolvePromise被调用,并且使用y作为value,就执行[](promise, y)。</p>
<p>      2.3.3.3.2&nbsp; 如果rejectPromise被调用,并且使用r作为reason,就让promise的状态变为rejected。</p>
<p>      2.3.3.3.3&nbsp; 如果同时调用<code class="highlighter-rouge">resolvePromise和</code><code class="highlighter-rouge">rejectPromise</code>或多次调用同一个,则第一个调用优先,并且任何进一步的调用都将被忽略。</p>
<p>      2.3.3.3.4&nbsp; 如果调用then方法抛出了一个错误e。</p>
<p>        2.3.3.3.4.1&nbsp; 如果<code class="highlighter-rouge">resolvePromise</code><span><span>或</span></span><code class="highlighter-rouge">rejectPromise已经被调用了,那么则忽略它。</code></p>
<p>        2.3.3.3.4.2&nbsp; 否则,把promise的状态变更为rejected,e作为reason。</p>
<p>    2.3.3.4&nbsp; 如果then不是一个一个函数,那么则把promise的状态变更为fulfilled,把x作为value。</p>
<p>  2.3.4&nbsp; 如果x不是一个函数或者对象,那么则把promise的状态变更为fulfilled,把x作为value。</p>
<p>  如果一个promise被一个参与循环的thenable链中的thenable所resolved,这样[](promise,thenable)的递归性质将最终导致再次调用[](promise,thenable),遵循上诉算法将导致无限递归。本规范鼓励但是并不要求一定要去检测这种无限递归,如果实现的话,那么此时promise的状态应该变成rejected并提供有效的错误信息作为reason。</p>
<p>  从实现上来说,不应该对可调用链的深度做任何限制,并假设超出该限制,递归将是无限的。只有确定的循环调用才会抛出TypeError。如果遇到无限的不同thenable链,则无限递归是正确的。</p>
<p>  好吧,这规范巴巴了好多。我建议要看一遍!我们继续上一小节的内容,去完善resolvePromise方法。</p>
<p>  首先2.3.1说了如果promise和x引用自同一个对象,那么抛出错误,这个简单,就这样被:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> resolvePromise(promise, x, resolve, reject) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (promise ==<span style="color: rgba(0, 0, 0, 1)"> x) {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> reject(
</span><span style="color: rgba(0, 128, 128, 1)">4</span>       <span style="color: rgba(0, 0, 255, 1)">new</span> TypeError("Chaining cycle detected for promise #&lt;Promise&gt;"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span></strong><span style="color: rgba(0, 128, 128, 1)">7</span> }</pre>
</div>
<p>  那这样的场景什么时候才会出现呢?为什么x和promise不能相等呢?我们先来看个例子,首先我们在判断条件中添加一个打印,确定走到这个逻辑了:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (promise ==<span style="color: rgba(0, 0, 0, 1)"> x) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">2</span>   console.log("进来了"<span style="color: rgba(0, 0, 0, 1)">);
</span></strong><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> reject(
</span><span style="color: rgba(0, 128, 128, 1)">4</span>       <span style="color: rgba(0, 0, 255, 1)">new</span> TypeError("Chaining cycle detected for promise #&lt;Promise&gt;"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)">6</span>   }</pre>
</div>
<p>  然后例子是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const p1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   resolve("success"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> let p2 = p1.then((res) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p2;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">p2.then(
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   console.log("成功"<span style="color: rgba(0, 0, 0, 1)">, value);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   (reason) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   console.log("失败"<span style="color: rgba(0, 0, 0, 1)">, reason);
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">15</span> );</pre>
</div>
<p>  执行这段代码,打印结果如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">进来了
失败 TypeError: Chaining cycle detected </span><span style="color: rgba(0, 0, 255, 1)">for</span> promise #&lt;Promise&gt;</pre>
</div>
<p>  说明我们的代码没问题,那么我们得来分析下这段代码是如何执行的。在声明p1的时候,我们直接执行了excutor的resolve,所以此时的promise就已经是fulfilled的状态了。当我们再去调用then方法的时候,then方法会返回个promise,此时由于已经是fulfilled状态了,所以会命中if (this.status === FULFILLED)条件,那么此时的x就是调用onFulfilled方法的结果,这个x最终的结果就是p2,所以此时resolvePromise(p, x, resolve, reject)方法中的p和x是同一个,那么在Promise内部,我们要等待p2的执行结果,<strong>那么此时p2就即是执行的过程,又是等待的结果</strong>,所以p2永远都不会处于结果状态,于是我们要特殊处理这样无法得到最终结果的情况,reject出去。理解了吧?</p>
<p>  我们继续,再往后是2.3.2的解释,这块的解释我们在写代码的时候可以不去考虑,因为在写2.3.3的时候,可以覆盖到2.3.2的逻辑。那么:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> resolvePromise(promise, x, resolve, reject) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (promise ==<span style="color: rgba(0, 0, 0, 1)"> x) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   console.log("进来了"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> reject(
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>       <span style="color: rgba(0, 0, 255, 1)">new</span> TypeError("Chaining cycle detected for promise #&lt;Promise&gt;"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">    );
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> ((<span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "object" &amp;&amp; x !== <span style="color: rgba(0, 0, 255, 1)">null</span>) || <span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">    resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span></strong><span style="color: rgba(0, 128, 128, 1)">12</span> }</pre>
</div>
<p>  着三行代码,就是2.3.3和2.3.4的逻辑框架,如果x是一个对象且不是null或者x是一个函数,那么要走一段逻辑,如果不符合这个条件的话说明是一个普通值,直接resolve就好了。这块很好理解,跟规范描述的一模一样。</p>
<p>  继续,2.3.3.1和2.3.3.2,当我们去取x.then的时候,可能会有报错,所以我们要try……catch处理一下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((<span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "object" &amp;&amp; x !== <span style="color: rgba(0, 0, 255, 1)">null</span>) || <span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   let then =<span style="color: rgba(0, 0, 0, 1)"> x.then
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">    reject(error)
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span></strong><span style="color: rgba(0, 128, 128, 1)">7</span> } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">8</span> <span style="color: rgba(0, 0, 0, 1)">resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">9</span> }</pre>
</div>
<p>  就是这样。继续2.3.3.3:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((<span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "object" &amp;&amp; x !== <span style="color: rgba(0, 0, 255, 1)">null</span>) || <span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   let then =<span style="color: rgba(0, 0, 0, 1)"> x.then;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> then === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">      then.call(
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      x,
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>         (y) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">          resolve(y)
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      },
</span><span style="color: rgba(0, 128, 128, 1)">10</span>         (r) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">          reject(r);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">      );
</span></strong><span style="color: rgba(0, 128, 128, 1)">14</span>   } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">17</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">    reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">20</span> } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">22</span> }</pre>
</div>
<p>  我们看这段代码,跟2.3.3.3所说是一模一样的。我们调用then方法,把x作为this并且传了onFulfilled, onRejected两个函数作为参数,那么基本的核心逻辑我们就处理完了,但是还要补全一些细节,比如2.3.3.3.3所说,如果多次调用,只取第一次的,后面的调用都应该被忽略,那这个逻辑我们要怎么处理呢?再比如2.3.3.3.1所说,then在call的时候,如果resolvePromise被调用,并且使用y作为value,就执行[](promise, y),而不是像我们上面那样,直接resolve就完事了。所以我们要把resolve(y)修改成resolvePromise(promise, y, resolve, reject)。因为我们可能再返回一个promise,直到最后不是一个promise,也就是规范中所说的,允许任意递归调用。</p>
<div>  那么我们继续处理2.3.3.3.3这种情况:</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ((<span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "object" &amp;&amp; x !== <span style="color: rgba(0, 0, 255, 1)">null</span>) || <span style="color: rgba(0, 0, 255, 1)">typeof</span> x === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let called = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span></strong><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   let then =<span style="color: rgba(0, 0, 0, 1)"> x.then;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> then === "function"<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      then.call(
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">      x,
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>         (y) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 9</span>         <span style="color: rgba(0, 0, 255, 1)">if</span> (called) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">10</span>         called = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span></strong><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">          resolvePromise(promise, y, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">      },
</span><span style="color: rgba(0, 128, 128, 1)">13</span>         (r) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)">14</span>         <span style="color: rgba(0, 0, 255, 1)">if</span> (called) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">15</span>         called = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span></strong><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">          reject(r);
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">      );
</span><span style="color: rgba(0, 128, 128, 1)">19</span>   } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">22</span>   } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><strong><span style="color: rgba(0, 128, 128, 1)">23</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (called) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">24</span>   called = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span></strong><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">    reject(error);
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">27</span> } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">resolve(x);
</span><span style="color: rgba(0, 128, 128, 1)">29</span> }</pre>
</div>
<p>  我们看代码,其实处理起来也并不复杂,我们在执行的时候,添加了一个flag,只要走到了某一个会修改promise状态的逻辑中,就会修改flag的状态,后续再调用就不会再去走逻辑代码了。也就是一旦状态确定,后续无论怎么调用,都是之前确定的状态,无法更改。</p>
<p>  那么其实到现在,核心的代码基本上就都完事了,其实你看,核心的代码其实就是resolvePromise这个方法,剩下的,我们还需要处理一点细节,比如2.2.1,onFulfilled, onRejected都是可选参数。我们稍微来处理一下then方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">then(onFulfilled, onRejected) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   onFulfilled = <span style="color: rgba(0, 0, 255, 1)">typeof</span> onFulfilled === "function" ? onFulfilled : (v) =&gt;<span style="color: rgba(0, 0, 0, 1)"> v;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   onRejected =
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">typeof</span> onRejected === "function"
<span style="color: rgba(0, 128, 128, 1)"> 5</span>       ?<span style="color: rgba(0, 0, 0, 1)"> onRejected
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       : (e) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>         <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> e;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      };
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 后面的略了</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> }</pre>
</div>
<p>  看到没,代码很简单,如果没传,我们就自己造一个就好了。最后我们就写完了这个Promise的实现,完整代码可在:https://github.com/zakingwong/zaking-js-advanced/tree/promise这里查看。</p>
<p>  那么最后,我们还可以使用社区的工具库,来测试下我们所写的代码是否符合规范。这个我就不多说了,具体可以参考gitHub上的代码。</p>
<p>  既然代码我们都写完了,来玩两个例子吧。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> const zp1 = <span style="color: rgba(0, 0, 255, 1)">new</span> ZakingPromise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   resolve("ok"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> }).then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(data);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">10</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">zp1.then(
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   console.log(data, "zp1-resolve"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   console.log(data, "zp1-reject"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">18</span> );</pre>
</div>
<p>  这里的ZakingPromise是我们自己手写的Promise,Promise就是ES6的Promise。我们来分析下这段代码,其实并不难噢。首先,ZakingPromise直接resolve出去了一个ok字符串,那么当我们调用then的时候,状态已经是fulfilled了,此时then里面执行的代码是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> let p = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> FULFILLED) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>       <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>         let x = onFulfilled(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value);
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      resolvePromise(p, x, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      reject(error);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   }, 0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 255, 1)">return</span> p</pre>
</div>
<p>  pending和rejected都跟我没关系。就执行了上面这点代码,那么这里的onFulfilled就是我们传进来的这段代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(data);
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">7</span> }</pre>
</div>
<p>  它返回了一个new Promise,所以此时的x可以理解为:</p>
<div class="cnblogs_code">
<pre>let x = <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
setTimeout(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    resolve(</span>'ok'<span style="color: rgba(0, 0, 0, 1)">);
}, </span>1000<span style="color: rgba(0, 0, 0, 1)">);
});</span></pre>
</div>
<p>  OK,那么我们继续,要去走resolvePromise这段代码了。那么resolvePromise判断逻辑就不走了哈,它肯定是个函数然后获取x.then,并且x.then肯定也是函数。那么里面的逻辑就相当于是这样执行的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   resolve("ok"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">}).then(
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   (y) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (called) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   called = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">    resolvePromise(promise, y, resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   (r) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (called) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   called = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">    reject(r);
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">16</span> );</pre>
</div>
<p>  这样是不是就很好理解了,那么我们就得去分析下上面的代码,还没调用then是如何执行的,就很简单了对吧,在Promise内部由于调用then的时候还没resolve,之前咱们分析过,所以then方法内存了两个缓存数组,当1秒后调用了resolve("ok")的时候,then里的onFulfilled回调就执行了,此时的y就是“ok”。注意,这里我省略了那个调用call时候的x,也就是此时then方法内部的this指向,那这个x、也就是this就是指这个new Promise他自己。</p>
<p>  于是我们再走这段代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">zp1.then(
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   console.log(data, "zp1-resolve"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">6</span>   console.log(data, "zp1-reject"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">8</span> );</pre>
</div>
<p>  就走了zp1的onFulfilled回调,于是就打印了“ok zp1-resolve”。行吧,例子就这一个了吧暂时,大家要去多练练例子啊,把复杂的情况都梳理梳理。</p>
</div>
<h2>五、实现Promise的其他方法</h2>
<p>   诶?我记得Promise还有catch方法、all方法、还有race、还有finally,这些方法你咋没写呢?嗯,首先前四章所有翻译的内容就是Promise规范的所有内容,不信的话我在文末贴出了Promise/A+规范的地址,也就是说规范中压根就没有这些方法,这些方法都是ES6额外实现的,那么接下来我们就来实现下这些方法。</p>
<h3>1、Promise.resolve、Promise.reject和Promise.catch方法的实现</h3>
<p>  我们先来看下Promise.resolve和Promise.reject,其实实现起来很简单哈,我们先写个例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> Promise.resolve("ok").then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   console.log(data, "resolve"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">3</span> });</pre>
</div>
<p>  我们看,调用Promise.resolve直接就返回了个promise的fulfilled状态,再去调用then的成功的回调,所以实现起来就是这样的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">static resolve(value) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)">    resolve(value);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">5</span> }</pre>
</div>
<p>  前面的那些代码我就不写了哈,但是这样还没完,我们再看个例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">Promise.resolve(
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">4</span>       resolve("Zaking"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">5</span>   }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">7</span> ).then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">8</span>   console.log(data, "resolve----"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">9</span> });</pre>
</div>
<p>  你猜,按照我们之前实现的代码,这个打印的结果是什么。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Promise {
status: </span>'PENDING'<span style="color: rgba(0, 0, 0, 1)">,
value: undefined,
reason: undefined,
onResolvedCallbacks: [],
onRejectedCallbacks: []
} resolve</span>----</pre>
</div>
<p>  为什么会这样呢?按道理不应该是Zaking这个字符串么?ES6实现的是这样的,但是咱们之前的代码并没有做这个的处理,其实这里的resolve方法,不就是包裹了一层Promise后执行了resolve嘛,所以当我们调用Promise.resolve的时候,上面例子的代码中,传给Promise.resolve的那个新的promise被当作value返回了,所以这里我们要添加点代码处理下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">class Promise {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">constructor(excutor) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   const resolve = (value) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 5</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> (value <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Promise) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>         <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> value.then(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span></strong><span style="color: rgba(0, 128, 128, 1)"> 8</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.status ===<span style="color: rgba(0, 0, 0, 1)"> PENDING) {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.value =<span style="color: rgba(0, 0, 0, 1)"> value;
</span><span style="color: rgba(0, 128, 128, 1)">10</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.status =<span style="color: rgba(0, 0, 0, 1)"> FULFILLED;
</span><span style="color: rgba(0, 128, 128, 1)">11</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.onResolvedCallbacks.forEach((cb) =&gt; cb(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.value));
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">    };
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> }</pre>
</div>
<p>  我们判断一下传入的value是不是一个Promise的实例,如果是的话,那么就再调用一下then,这样就可以把结果传递到下一层了。这样,我们打印的结果就符合ES6的实现了,注意,这个跟规范无关了噢。</p>
<div class="cnblogs_code">
<pre>Zaking resolve----</pre>
</div>
<p>  你猜Promise.reject这个方法怎么实现?我不写了噢,你自己试试!要注意的是,resolve一个Promise会等待解析后的结果,但是reject一个Promise会直接走向失败。</p>
<p>  那么接下来我们来实现一个更简单的方法,Promise.catct:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)">(errCb) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.then(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">, errCb);
</span><span style="color: rgba(0, 128, 128, 1)">3</span> }</pre>
</div>
<p>  就这么简单,其实catch方法本质上就是then中的onRejected这个回调,那么我们直接调用就好了,不传onFulfilled只传onRejected。</p>
<h3>2、Promise.all的实现</h3>
<p>  all方法其实很好理解,就是所有的回调都成功了,才算是成功,我们看个例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">Promise.all([
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   resolve("1"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">}),
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   resolve("2"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}),
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> ]).then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   console.log(data, "all"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">10</span> });</pre>
</div>
<p>  就是这样,all方法会接收一个都是Promise的数组,然后内部会去处理这个数组,我们就来看看是怎么处理的吧:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">static all(promises) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let result =<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   let times = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> processResult(data, index) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       result =<span style="color: rgba(0, 0, 0, 1)"> data;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> (++times ===<span style="color: rgba(0, 0, 0, 1)"> promises.length) {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(result);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i &lt; promises.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       const promise =<span style="color: rgba(0, 0, 0, 1)"> promises;
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       Promise.resolve(promise).then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">      processResult(data, i);
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">      }, reject);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">18</span> }</pre>
</div>
<p>  其实你看代码并不多的,我们来分析下吧。首先我们声明了两个变量,一个存储结果的数组,这个结果是指resolve成功后的onFulfilled回调的value,一个是计数器。然后我们去循环整个传入的promises,让每一个promise传递给Promise.resolve去执行,然后我们用一个processResult去处理返回的data和当前的下标i,然后我们还要处理一下reject,那么这里要注意哈,对于all方法来说,只要有一个出错了,那么整个all执行的结果就是rejected的,所以reject不用特殊处理,直接reject就好了,不需要添加进数组这个那个的。</p>
<p>  那么继续,processResult每当我们执行一次的时候,就会根据传入的下标的位置去存储结果,这样处理其实就是为了按照传入promise的先后顺序去存储结果,然后先++times再去和promises的length长度去做比较,换句话说就是计数嘛,如果相等,就直接resolve最终的result即可。并不是很复杂噢。</p>
<h3>3、Promise.race的实现</h3>
<p>  这个race是什么意思呢,我们先看个例子:</p>
<div class="cnblogs_code">
<pre>const p = Promise.race();</pre>
</div>
<p>  上面代码中,只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个实例率先改变状态,<code>p</code>的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给<code>p</code>的回调函数。换句话说就是,只要有一个结果就行了,不管这个结果是啥。谁跑得快我就算谁是第一,这就是race的意思。那既然是谁快我选谁,那我们代码要怎么写?循环一下全部执行一遍呗,谁有结果了就完事了呗:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">static race(promises) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i &lt; promises.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)">4</span>       let promise =<span style="color: rgba(0, 0, 0, 1)"> promises;
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">      Promise.resolve(promise).then(resolve, reject);
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">8</span> }</pre>
</div>
<p>  嗯……就这么简单,其实就是all的那部分删除了一些~~</p>
<h3>4、Promise.allSettled的实现</h3>
<p>  这个方法是啥意思呢,就是不管成功或失败,会返回所有异步的结果。诶?那不是跟all方法很类似,只要修改一下all方法不就可以了,没错,你可真是个小聪明:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">static allSettled(promises) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let result =<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   let times = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><strong><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> processResult(data, index, status) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       result =<span style="color: rgba(0, 0, 0, 1)"> { status, value: data };
</span></strong><span style="color: rgba(0, 128, 128, 1)"> 7</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> (++times ===<span style="color: rgba(0, 0, 0, 1)"> promises.length) {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(result);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i &lt; promises.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       const promise =<span style="color: rgba(0, 0, 0, 1)"> promises;
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">      Promise.resolve(promise).then(
</span><span style="color: rgba(0, 128, 128, 1)">14</span>         (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>         processResult(data, i, "fulfilled"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">      },
</span><strong><span style="color: rgba(0, 128, 128, 1)">17</span>         (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>         processResult(err, i, "rejected"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span></strong><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">      );
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 128, 1)">23</span> }</pre>
</div>
<p>  你看跟all方法有啥区别?无非就是额外处理了onRejected,直接onRejected就直接失败了,现在回去走processResult方法也存到result中,当然,result存的内容也不太一样,多了个状态,会告诉你是失败的结果还是成功的结果。不复杂吧,其实就是all方法。</p>
<h3>5、Promise.finally的实现</h3>
<p>  finally方法用于指定不管Promise对象最后的状态如何,都会执行的操作,就是我不管你Promise最后是fulfilled还是rejected,都会执行finally的回调。那我们来看是咋实现的吧:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">(finallyCallback) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   let p = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.constructor;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.then(
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>       <span style="color: rgba(0, 0, 255, 1)">return</span> p.resolve(finallyCallback()).then(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> data);
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">    },
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       <span style="color: rgba(0, 0, 255, 1)">return</span> p.resolve(finallyCallback()).then(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>         <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> err;
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      });
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">13</span> }</pre>
</div>
<p>  其实代码不难,但是这里面还是有点东西的。为什么我在this.then中又调用了p.resolve并且传了finally的finallyCallback回调?我直接这样写不行么?</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">(finallyCallback) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.then(
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">      finallyCallback()
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>       <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> data;
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">    },
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      finallyCallback()
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> err;
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> }</pre>
</div>
<p>  调用finallyCallback,返回结果。好像挺完美的。但是,不太行,因为按照上面的finally实现,你这样去写Promise的用法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> Promise.resolve("zaking-finally"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   .<span style="color: rgba(0, 0, 255, 1)">finally</span>(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   console.log("finally~~~"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   .then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   console.log(data, "finally-resolved"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   .<span style="color: rgba(0, 0, 255, 1)">catch</span>((err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   console.log(err, "finally-rejected"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   });</pre>
</div>
<p>  或者这样:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> Promise.resolve("zaking-finally"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   .<span style="color: rgba(0, 0, 255, 1)">finally</span>(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   console.log("finally~~~"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> 1<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   .then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   console.log(data, "finally-resolved"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   .<span style="color: rgba(0, 0, 255, 1)">catch</span>((err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   console.log(err, "finally-rejected"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   });</pre>
</div>
<p>  都没问题,但是因为finallyCallback有可能是异步的,所以我们需要额外的包裹一层,再去执行最终的onFulfilled或者onRejected。比如这样:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> Promise.resolve("zaking-finally"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   .<span style="color: rgba(0, 0, 255, 1)">finally</span>(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>       setTimeout(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">      resolve();
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>         console.log("finally"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       }, 1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">    });
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   .then((data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   console.log(data, "finally-resolved"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">})
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   .<span style="color: rgba(0, 0, 255, 1)">catch</span>((err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   console.log(err, "finally-rejected"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">15</span>   });</pre>
</div>
<p>  那么本章所有的内容就都完事了,当然我们并没有实现所有的方法,如果你真的理解了,完全可以自己去实现了,比如any方法和try方法,甚至于你可以自己去创造某些方法,因为Promise/A+规范范围内的所有内容,其实就那么点,其他的都是实现罢了,你看我讲了五个方法,实际上只讲了all和finally这两个方法。</p>
<h3>6、promisify</h3>
<p>  最后我们来聊一聊promisify,也是这篇文章最后的一点内容,promisify其实是Node.js提供的可以把普通带回调的函数,转换成promise对象的工具方法。我们先来看个例子,怎么用promisify:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> testA(a, b, cb) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   cb(<span style="color: rgba(0, 0, 255, 1)">null</span>, a +<span style="color: rgba(0, 0, 0, 1)"> b);
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> let A =<span style="color: rgba(0, 0, 0, 1)"> promisify(testA);
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> A(1, 2<span style="color: rgba(0, 0, 0, 1)">).then(
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   (data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   console.log(data, "data-----"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   (err) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    console.log(err);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">13</span> );</pre>
</div>
<p>  上面的代码,首先testA是一个带有回调函数作为参数的方法,当然这是一个同步回调,然后要注意的是,cb在testA中必须是最后一个参数,而执行cb的时候,第一个参数必须是错误处理的回调函数,这里我们直接用null代替了,然后,我们通过promisify方法,包裹了一下testA,生成了一个A方法,那么此时的A就是一个Promise对象了噢,然后调用Promise的then方法,传入onFulfilled和onRejected两个回调,其实这里的onFulfilled就可以理解成是cb,而onRejected就是那个null。那么我们来看下实现,代码很少:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> promisify(fn) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (...args) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>       fn(...args, <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (err, data) {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>         <span style="color: rgba(0, 0, 255, 1)">if</span> (err) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> reject(err);
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      resolve(data);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">      });
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">    });
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 128, 1)">10</span> }</pre>
</div>
<p>  好嘞,那么我们依照实现和例子代码,我们来分析下这个promisify是如何运行的。首先,promisify返回了一个函数,那么这个返回的函数,就是我们例子中的A方法,A传入的参数1,2也同样在promisify返回的function中通过rest参数来处理的,从而获取到我们传入的参数。然后,我们在返回的函数内部又返回了一个Promise。我们先不管这个Promise,咱们删除点东西再看一下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> promisify(fn) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (...args) {
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Promise((resolve, reject) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">4</span>       fn(<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">5</span> <span style="color: rgba(0, 0, 0, 1)">    });
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 128, 1)">7</span> }</pre>
</div>
<p>  我们看,其实就是在返回的Promise内部执行了一下我们传入的testA方法,只不过他回调前面的参数都用rest参数的方式获取到了,那么我们testA的第三个参数就是这里的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (err, data) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> (err) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> reject(err);
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)">    resolve(data);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> }</pre>
</div>
<p>  这一部分,就是testA内部执行的cb了呗。err就是我们传的null,data就是那个a + b。简单吧?其实一点都不复杂。</p>
<p>  那你可能会问为啥要这样固定的去写testA方法呢,回调必须是最后一个参数,错误的回调还必须是回调函数的第一个参数,嗯。。是因为node的实现是这样的~~</p>
<p>  最后,再补充一点,这个东西理解了promisify后就很简单了,不多说了,也就是promisifyAll方法,接收一个对象作为参数:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> promisifyAll(obj) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>   let result =<span style="color: rgba(0, 0, 0, 1)"> {};
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">for</span> (let key <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> obj) {
</span><span style="color: rgba(0, 128, 128, 1)">4</span>   result =
<span style="color: rgba(0, 128, 128, 1)">5</span>       <span style="color: rgba(0, 0, 255, 1)">typeof</span> obj === "function" ?<span style="color: rgba(0, 0, 0, 1)"> promisify(obj) : obj;
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">7</span>   <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
</span><span style="color: rgba(0, 128, 128, 1)">8</span> }</pre>
</div>
<p>  很简单,就是生成一个新的对象result,如果传入的obj中的某个key是函数,就走一下promisify方法转换,最终返回result结果对象。</p>
<p>&nbsp;</p>
<p><strong>  附:</strong></p>
<ul>
<li>https://promisesaplus.com/</li>
<li>https://es6.ruanyifeng.com/#docs/promise</li>
<li>https://github.com/zakingwong/zaking-js-advanced/tree/promise</li>
</ul>
<p>&nbsp;</p>
<p>  <strong>最后:由于本人能力有限,感觉写的内容并未完全覆盖使用场景,大家见谅,但是还是有参考价值的~额~~然后我第一次尝试加行号,结果博客园的行号会一起复制下来,不太友好,所以特别建议大家手打代码,嘿嘿。</strong></p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:Zaking,转载请注明原文链接:https://www.cnblogs.com/zaking/p/16467314.html</p><br><br>
来源:https://www.cnblogs.com/zaking/p/16467314.html
頁: [1]
查看完整版本: Javascript之我也来手写一下Promise