在本教程中,您将学习有关在 JavaScript 中使用 Promise 和 async/await 所需了解的所有内容。
那么让我们开始吧。
为什么在 JavaScript 中使用 Promise?
ES6 引入了 Promise 作为原生实现。在 ES6 之前,我们使用回调来处理异步操作。
让我们了解什么是回调以及 Promise 解决了哪些与回调相关的问题。
假设我们有一个帖子列表及其各自的评论,如下所示:
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> posts <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#999999">[</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">1</span><span style="color:#999999">,</span> <span style="color:#990055">post_title</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'First Post'</span> <span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">2</span><span style="color:#999999">,</span> <span style="color:#990055">post_title</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'Second Post'</span> <span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">3</span><span style="color:#999999">,</span> <span style="color:#990055">post_title</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'Third Post'</span> <span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">]</span><span style="color:#999999">;</span>
<span style="color:#0077aa">const</span> comments <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#999999">[</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">2</span><span style="color:#999999">,</span> <span style="color:#990055">comment</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'Great!'</span><span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">2</span><span style="color:#999999">,</span> <span style="color:#990055">comment</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'Nice Post!'</span><span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">{</span> <span style="color:#990055">post_id</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#990055">3</span><span style="color:#999999">,</span> <span style="color:#990055">comment</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">:</span></span> <span style="color:#669900">'Awesome Post!'</span><span style="color:#999999">}</span><span style="color:#999999">,</span>
<span style="color:#999999">]</span><span style="color:#999999">;</span></code></span>
现在,我们将编写一个函数,通过传递帖子 ID 来获取帖子。如果找到该帖子,我们将检索与该帖子相关的评论。
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> <span style="color:#dd4a68">getPost</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#999999">(</span>id<span style="color:#999999">,</span> callback<span style="color:#999999">)</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> <span style="color:#999999">{</span>
<span style="color:#0077aa">const</span> post <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> posts<span style="color:#999999">.</span><span style="color:#dd4a68">find</span><span style="color:#999999">(</span> post <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> post<span style="color:#999999">.</span>post_id <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">===</span></span> id<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#0077aa">if</span><span style="color:#999999">(</span>post<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">callback</span><span style="color:#999999">(</span><span style="color:#0077aa">null</span><span style="color:#999999">,</span> post<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span> <span style="color:#0077aa">else</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">callback</span><span style="color:#999999">(</span><span style="color:#669900">"No such post found"</span><span style="color:#999999">,</span> <span style="color:#0077aa">undefined</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#999999">}</span><span style="color:#999999">;</span>
<span style="color:#0077aa">const</span> <span style="color:#dd4a68">getComments</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#999999">(</span>post_id<span style="color:#999999">,</span> callback<span style="color:#999999">)</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> <span style="color:#999999">{</span>
<span style="color:#0077aa">const</span> result <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> comments<span style="color:#999999">.</span><span style="color:#dd4a68">filter</span><span style="color:#999999">(</span> comment <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> comment<span style="color:#999999">.</span>post_id <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">===</span></span> post_id<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#0077aa">if</span><span style="color:#999999">(</span>result<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">callback</span><span style="color:#999999">(</span><span style="color:#0077aa">null</span><span style="color:#999999">,</span> result<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span> <span style="color:#0077aa">else</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">callback</span><span style="color:#999999">(</span><span style="color:#669900">"No comments found"</span><span style="color:#999999">,</span> <span style="color:#0077aa">undefined</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>
在上面的getPost
函数中getComments
,如果出现错误,我们会将其作为第一个参数传递。但是如果我们得到结果,我们将调用回调函数并将结果作为第二个参数传递给它。
如果您熟悉 Node.js,那么您就会知道这是每个 Node.js 回调函数中使用的非常常见的模式。
现在让我们使用这些函数:
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#dd4a68">getPost</span><span style="color:#999999">(</span><span style="color:#990055">2</span><span style="color:#999999">,</span> <span style="color:#999999">(</span>error<span style="color:#999999">,</span> post<span style="color:#999999">)</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> <span style="color:#999999">{</span>
<span style="color:#0077aa">if</span><span style="color:#999999">(</span>error<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#0077aa">return</span> console<span style="color:#999999">.</span><span style="color:#dd4a68">log</span><span style="color:#999999">(</span>error<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
console<span style="color:#999999">.</span><span style="color:#dd4a68">log</span><span style="color:#999999">(</span><span style="color:#669900">'Post:'</span><span style="color:#999999">,</span> post<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">getComments</span><span style="color:#999999">(</span>post<span style="color:#999999">.</span>post_id<span style="color:#999999">,</span> <span style="color:#999999">(</span>error<span style="color:#999999">,</span> comments<span style="color:#999999">)</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=></span></span> <span style="color:#999999">{</span>
<span style="color:#0077aa">if</span><span style="color:#999999">(</span>error<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#0077aa">return</span> console<span style="color:#999999">.</span><span style="color:#dd4a68">log</span><span style="color:#999999">(</span>error<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
console<span style="color:#999999">.</span><span style="color:#dd4a68">log</span><span style="color:#999999">(</span><span style="color:#669900">'Comments:'</span><span style="color:#999999">,</span> comments<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span></code></span>
执行上述代码后,您将看到以下输出:
调用 getPost 和 getComments 函数的结果
这是一个CodePen 演示。
正如您所看到的,我们将getComments
函数嵌套在getPost
回调中。
现在想象一下,如果我们也想找到这些评论的点赞。这也会嵌套在getComments
回调中,从而创建更多嵌套。这最终会让代码变得难以理解。
这种回调嵌套称为回调地狱。
您可以看到错误处理条件也在代码中重复,这会创建重复的代码 - 这不好。
因此,为了解决这个问题并允许异步操作,引入了 Promise。
JavaScript 中的 Promise 是什么?
Promise 是 JavaScript 最重要的部分之一,但它们可能会令人困惑且难以理解。许多新开发人员以及经验丰富的开发人员都很难完全掌握它们。
那么什么是承诺呢?Promise 代表一个异步操作,其结果将在将来出现。
在 ES6 之前,没有办法等待某些东西来执行某些操作。例如,当我们想要进行API调用时,没有办法等到结果返回。
为此,我们曾经使用 JQuery 或 Ajax 等外部库,它们有自己的 Promise 实现。但 Promise 没有 JavaScript 实现。
但后来在 ES6 中添加了 Promise 作为原生实现。现在,使用 ES6 中的 Promise,我们可以自己进行 API 调用,并等待它完成后再执行某些操作。
如何创建承诺
要创建 Promise,我们需要使用Promise
如下的构造函数:
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> promise <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">Promise</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span>resolve<span style="color:#999999">,</span> reject<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span></code></span>
构造Promise
函数将函数作为参数,该函数在内部接收resolve
和reject
作为参数。
和参数实际上是我们可以根据异步操作的结果调用的函数resolve
。reject
APromise
可以经历三种状态:
- 待办的
- 实现了
- 拒绝
当我们创建一个承诺时,它处于待处理状态。当我们调用该resolve
函数时,它会进入已完成状态,如果我们调用reject
它,它将进入拒绝状态。
为了模拟长时间运行或异步操作,我们将使用该setTimeout
函数。
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> promise <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">Promise</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span>resolve<span style="color:#999999">,</span> reject<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">setTimeout</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#0077aa">const</span> sum <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#990055">4</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">+</span></span> <span style="color:#990055">5</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">resolve</span><span style="color:#999999">(</span>sum<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">,</span> <span style="color:#990055">2000</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
</code></span>
在这里,我们创建了一个承诺,它将在 2000 毫秒(2 秒)超时结束后解析为 和 的总和4
。5
为了获得成功执行 Promise 的结果,我们需要使用.then
如下方式注册一个回调处理程序:
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> promise <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">Promise</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span>resolve<span style="color:#999999">,</span> reject<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">setTimeout</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#0077aa">const</span> sum <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#990055">4</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">+</span></span> <span style="color:#990055">5</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">resolve</span><span style="color:#999999">(</span>sum<span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">,</span> <span style="color:#990055">2000</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
promise<span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span>result<span style="color:#999999">)</span> <span style="color:#999999">{</span>
console<span style="color:#999999">.</span><span style="color:#dd4a68">log</span><span style="color:#999999">(</span>result<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:#708090">// 9</span>
<span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
</code></span>
因此,每当我们调用 时resolve
,promise 都会返回传递给resolve
函数的值,我们可以使用.then
处理程序收集该值。
如果操作不成功,那么我们reject
这样调用该函数:
<span style="color:var(--gray85)"><code class="language-js"><span style="color:#0077aa">const</span> promise <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">Promise</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span>resolve<span style="color:#999999">,</span> reject<span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">setTimeout</span><span style="color:#999999">(</span><span style="color:#0077aa">function</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#0077aa">const</span> sum <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">=</span></span> <span style="color:#990055">4</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">+</span></span> <span style="color:#990055">5</span> <span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">+</span></span> <span style="color:#669900">'a'</span><span style="color:#999999">;</span>
<span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="color:#dd4a68">isNaN</span><span style="color:#999999">(</span>sum<span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
<span style="color:#dd4a68">reject</span><span style="color:#999999">(</span><span style="color:#669900">'Error while calculating sum.'</span><span style="color:#999999">)</span><span style="color:#999999">;</span&