如今,Web 开发已经成为了一个极具挑战性的任务。客户端和服务器端的开发团队都要面对严峻的要求,不断地优化应用的性能和用户体验。在这项挑战中,值得注意的是使用 Promise,这是 ES6 中的一个新特性,它可以帮助我们避免由于异步任务带来的一些繁琐的问题。本文将为您介绍 ES6 中的 Promise,包括如何创建、链式调用和其遇到的一些常见问题以及解决方法。
什么是 Promise?
Promise 是一个表示异步操作的对象,Promise 在异步操作完成后可以return一个成功/失败的回调函数。
使用 Promise 可以消除回调函数的嵌套使用,它通过提供可用于将多个回调链接在一起的机制,为异步编程提供了优雅且可读性高的代码编写方式。
Promise 基础
创建一个 Promise
一个 Promise 对象表示一个异步操作,有三种状态:
- pending(进行中)
- resolved(已成功)
- rejected(已失败)
一个 Promise 对象创建后,一开始是 pending 状态,等待异步任务完成后自动进入 resolved 或 rejected 状态。
const promise = new Promise((resolve, reject) => { // 执行异步任务,同步代码会马上执行,异步代码会进入事件循环 if (任务成功) { resolve('数据准备就绪'); // resolve 函数完成,将 Promise 对象状态改为 resolved } else { reject('任务失败,请重试'); // reject 函数完成,将 Promise 对象状态改为 rejected } })
我们来看一个简单的 Promise 示例:
-- -------------------- ---- ------- ----- ------- - --- ----------------- ------- -- - ------------- -- - ------------------ -- ------ --- --------------------- -- - -------------------- ---------------- -- - ------------------- ---
在这个示例中,Promise 对象的状态开始时为 pending,它的 resolve 函数设定的延迟时间为 1 秒钟。当 1 秒钟到达时,状态改变为 resolved,并将数据准备就绪传递给 then 方法,打印出 "数据准备就绪"。
链式调用 Promise
Promise 可以链式调用,并保证异步回调的执行顺序。即使回调函数是异步的,Promise 每次都会返回一个新的 Promise 对象,使得每一个回调的执行顺序依然可以执行。这种技术被称为 Promise 链。
-- -------------------- ---- ------- ---------------- -------- -- - ----------------- - ---------- ------ ----------------- -- -------------- -- - ----------------- - ---------- -------------------- -- -------------- -- - ------------------- ---
在这个示例中,当我们调用 doPromiseTask1 函数时,它返回一个 Promise 对象并执行异步任务。当 Promise 对象达到 resolved 状态时,then 链中的匿名函数执行,打印出 "task 1 success" 并调用 doPromiseTask2 函数。在 doPromiseTask2 函数中,它返回一个 Promise 对象,正如所料,它也会执行异步任务。同样,当任务达到 resolved 状态时,then 链中的匿名函数执行,打印出 "task 2 success" 以及数据信息。
Promise.all 和 Promise.race
Promise.all 方法接受多个 Promise 对象并返回一个新的 Promise 对象,它将在所有的传入 Promise 对象都已经 resolved 以后做出响应。如果其中任何一个 Promise 对象进入 rejected 状态,Promise.all 方法都将返回失败。它的语法如下所示:
-- -------------------- ---- ------- ------------- ----------------- ----------------- ---------------- ---------------- -- - ---------------------------------- ---------------------- ---------- ----------- ---------------- -- - ------------------- ---
在这个示例中,我们调用了三个函数并将它们放在数组中。当所有的函数达到 resolved 状态时,then 中的匿名函数会执行,访问原始数据并在控制台中打印。
Promise.race 方法将返回第一个达到 resolved 或 rejected 状态的 Promise 对象。例如,在查询某个服务的所有服务器上的响应时间时,可以使用race。 它的语法如下所示:
-- -------------------- ---- ------- -------------- ----------------- ----------------- ---------------- ---------------- -- - -------------------------- -------------------- ---------------- -- - ------------------- ---
在这个示例中,我们调用了三个函数并将它们放在数组中。当某个函数达到 resolved 状态时,then 中的匿名函数会执行,并访问该数据并在控制台中打印。
Promise 进阶
Promise 的错误处理
当 Promise 对象成功时,执行 then 方法,如果执行失败,则执行 catch 方法。
-- -------------------- ---- ------- ----- ------- - --- ----------------- ------- -- - -- ---------------------------- -- ------ - ------------------ - ---- - ------------------- - --- --------------------- -- - -------------------- ---------------- -- - ------------------- ---
在这个示例中,当一个 Promise 对象在执行任务的时间段结束后,resolve 或 reject 都会被执行。如果任务成功,则执行 then 实现中的回调函数,打印出 "数据准备就绪"。如果任务失败,则执行 catch 实现中的回调函数,打印出 "任务失败,请重试"。
Promise 的取消
Promise 规范中并没有取消 Promise 的方法,但我们可以通过调用 Promise.then() 的时候返回一个 Promise 函数,然后在 Promise 函数执行前通过另一个 Promise 函数进行中断。
-- -------------------- ---- ------- --- ---------- - ------ ----- ------- - --- ----------------- ------- -- - -- ---------------------------- -- ------------ - ---------- -------------- ------- ------- - -- ------ - ------------------ - ---- - ------------------- - --- ----- ------------- - --- ----------------- ------- -- - ------------- -- - ---------- - ----- ---------- -- ------ --- -------------- -------- ------------- ---------------- -- - -------------------- ---------------- -- - ------------------- ---
在这个示例中,当一个 Promise 对象在执行异步任务的时间段结束后,resolve 或 reject 都会被执行。但是,我们想要在某些时间中断 Promise,我们可以创建一个 cancelPromise,5 秒后它将执行。让它与其他的 Promise 对象一起活跃,直到其中的一个达到 resolved 状态,最终写入的状态被解析到 then 或 catch 方法中。
Promise 的 finally
在 ES2020 中,finally 方法被引入用于在 Promise 执行结束时调用,无论其状态成功或失败都会被调用。通过 finally 方法可以在执行后关闭资源、清理垃圾等操作。
promise.then((result) => { console.log(result); }).catch((error) => { console.log(error); }).finally(() => { console.log('Promise 执行结束'); });
在这个示例中,Promise 执行完毕后不管成功还是失败都将执行 finally 方法并打印出 "Promise 执行结束",这样我们可以在 finally 中完成一些清理操作。
真实的应用示例
在现实世界中,我们通常会被一些异步服务器和第三方 API 调用所困扰,而使用 Promise 可以解决许多与这些外部源的异步交互相关的问题。
-- -------------------- ---- ------- -------- -------------------------- - ------ --- ----------------- ------- -- - ----- ------ - --------------------------------- ----------- - ------------------ ------------ - ----- ---------- - ---------- ------------- - -------- -------------- - ------- ---------------------------------- --- - ------------------------------------------------------- -- - ------------------- ---------- ---------------------------- ---- - --------- -------------- --------- -- -- - --------------------- ------- - --- ----------- -- - ------------------- --- --- --------- ---
在这个示例中,我们使用 addScriptToPage 函数来处理异步加载外部脚本的问题。可以看出,当我们在页面中载入 Google API 时,异步加载成功后,我们将会看到控制台显示 "google api 加载成功"。而在控制台中打印 "google api NOT loaded" 则意味着没有正确地加载 Google API。
结论
Promise 给 Web 开发带来了很多好处。利用 Promise,我们可以防止回调地狱,轻松且可读性高地实现异步代码。我们可以使用 then、catch 和优秀示例中介绍的其他方法在控制台中处理异步任务执行后的数据。 但是,在实践中,也有可能遇到缺乏资源,取消某些任务或需要长时间运行的一些问题。在这种情况下我们需要对代码做出一些修改,以解决这些问题。通过使用 Promise,我们能够大幅度提高 Web 应用程序的可维护性和可读性,并使应用程序更容易扩展和保持更新。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/672b2abbddd3a70eb6d1f4f4