发布于 2026-01-06 2 阅读
0

JavaScript Promise 101 什么是 Promise? 使用新的 Promise Promise + setTimeout 连续调用 API 调用 捕获错误和拒绝 处理不同的拒绝方法 更多 resolve、reject 和 catch 示例 资源/更多阅读材料 由 Mux 呈现的 DEV 全球展示挑战赛:展示你的项目!

JavaScript Promise 101

什么是Promise

使用new Promise

Promise + setTimeout

连续链

API 调用

捕获错误和处理拒绝

不同的拒绝方法

更多解决、拒绝、捕获示例

资源/更多阅读材料

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

了解PromiseJavaScript 的工作原理将极大地提升你的开发技能。以下我将分享:

  1. 基本Promise
  2. 如何使用then
  3. catch以及错误处理

我保证这不会像你想象的那么难!🤓

什么是Promise

根据MDN 的说法:

Promise 对象表示异步操作的最终完成(或失败)及其结果值。

简单来说,Promise 是一个 JavaScript 对象。它在声明时没有值,但会在未来的某个时间点获得一个值。这个值要么被解析,要么被拒绝。

假设你从 dev.to 订购了一件新的连帽衫。购买后,从技术上讲,它就属于你了,但实际上你并没有真正拥有它,因为你还没有收到实物,只是收到了送货的承诺。这件连帽衫的状态随时可能变化:已送达、正在配送或丢失。请注意,即使连帽衫已经送达,如果尺码不合适或收到的商品与订单不符,你也可以选择拒收。

就像这件连帽衫一样,Promise 在任何时候都有 3 种状态:已完成、已拒绝、待处理。

使用new Promise

让我们开始使用 Promise 吧!

let foo = new Promise((resolve, reject) => {resolve('foo')})
foo.then(value => console.log(value) // foo
Enter fullscreen mode Exit fullscreen mode

我们可以使用简写方式来简化它Promise.resolve。以下与上述等效:

let foo = Promise.resolve('foo')
foo.then(value => console.log(value)) // foo
Enter fullscreen mode Exit fullscreen mode

Promise + setTimeout

让我们添加超时机制来模拟异步操作:

let promise1 = new Promise((resolve, reject) => {
  setTimeout(function() {
    resolve('foo');
  }, 2000)
})
promise1.then(val => console.log(val)) 
console.log("I promise I'll be first!")
// I promise I'll be first!
// ... 2 secs later  ¯\_(ツ)_/¯
// foo
Enter fullscreen mode Exit fullscreen mode

注意对数的顺序。

一些说明:

  1. 一旦声明了承诺(new Promise(...)),时间就开始倒计时。
  2. promise1它本身就是一个 Promise 对象。你可以在控制台中看到它:promise1 // Promise {<resolved>: "foo"}
  3. 你可以使用then`.`(或其他异步方法,但这又是另一个话题了)来访问“foo”。我的意思是,你不能直接console.log(promise1)在全局作用域中访问字符串“foo”。你需要把它放在 `.`console.log()内部then

连续链

Promise 可以链式调用,允许你进行连续的 Promise 调用。

let hello1 = new Promise(resolve => resolve("hello1"))

hello1.then(val1 => {
  console.log(val1);
  return "hello2"
}).then(val2 => {
  console.log(val2);
  return "hello3"
}).then(val3 => {
  console.log(val3)
})
// hello1
// hello2
// hello3
Enter fullscreen mode Exit fullscreen mode

你会注意到,在执行完 `hello1` 之后then,我又return执行了 `hello2`。这里的 `hello2` 是 `val2` 的值。第二个 `then` 也一样then,它返回 `hello3`,也就是 `val3` 的值。注意,要在 Promise 链中传递参数,前一个 Promisethen必须有返回值。如果没有返回值,下一个 Promise 将没有参数。

我的意思是:

hello1.then(val1 => {
  console.log(val1);
  return "hello2"
}).then(val2 => {
  console.log(val2); // no return
}).then(val3 => { 
  console.log(val3); // val3 is undefined
})
// hello1, hello2, undefined
Enter fullscreen mode Exit fullscreen mode

链继续,但 val3 没有值,因为前一个链未能提供返回值。

API 调用

我将仅简要介绍如何使用 Promise 进行 API 调用,因为它与 `.` 类似setTimeout。我们来使用 `.` fetch,因为它内置了 `.` 函数(你可以在 Chrome 控制台中体验它!)。以下代码来自typicode 网站

let fetchTodo = fetch('https://jsonplaceholder.typicode.com/todos/1')

fetchTodo // Promise {<pending>}

fetchTodo
  .then(response => response.json())
  .then(json => console.log(json))
Enter fullscreen mode Exit fullscreen mode

当我们第一次使用 API 调用时fetchTodo = fetch('https://jsonplaceholder.typicode.com/todos/1'),它会返回一个 Promise。

我们现在知道如何处理 promise 对象了——就是then这样!

捕获错误和处理拒绝

还记得 new Promise 的第二个参数吗?假设我们不满意异步操作的结果,我们可以拒绝它,而不是解析它。

let fooReject = new Promise((resolve, reject) => {reject('foo rejected')})
fooReject // Promise {<rejected>: "error foo"}
Enter fullscreen mode Exit fullscreen mode

养成及时发现 Promise 中错误的好习惯非常重要。一般来说👍:

then从现在开始,每次我们使用时,总是总是要有一个catch

let foo = new Promise((resolve, reject) => {reject('error foo')})
foo.then(value => console.log(value)).catch(err => console.log(err)) //gotta catch 'em all!
foo //error foo
Enter fullscreen mode Exit fullscreen mode

刚才发生了什么?

让我们比较一下,如果我们只放入then“无”这个选项会是什么情况。catch

foo = new Promise((resolve, reject) => {reject('error foo')})
foo.then(val => console.log(val))
// Promise {<rejected>: "error foo"}

Enter fullscreen mode Exit fullscreen mode

啊,我的 Chrome 控制台报错了,提示有个错误没有被捕获。我们需要捕获这个错误。让我们来捕获它!

foo.then(val => console.log(val)).catch(err => console.log(err)) // error foo
Enter fullscreen mode Exit fullscreen mode

现在我们看到更清晰的日志了!

不同的拒绝方法

你可能会问:“嘿,伙计,如果我有一条项链怎么办?”

let promise1 = new Promise(fetchSomeApi);
promise
  .then(processApi)
  .then(fetchApi2)
  .then(processApi2)
  .catch(handleCommonError)
Enter fullscreen mode Exit fullscreen mode

“我想对processApi剩余的错误做些不同的处理,让 handleCommonError 函数来处理这些错误?”

幸运的是,捕获错误的方法不止一种!then接受第二个参数。

回顾一下我们上面的第一段代码:let foo = new Promise((resolve, reject) => {resolve('foo')})。我们将使用reject它进行自定义错误处理。

你可以这样做:

promise
  .then(processApi)
  .then(fetchApi2, customErrorHandling)
  .then(processApi2)
  .catch(handleCommonError)
Enter fullscreen mode Exit fullscreen mode

如果在执行过程中出现错误processApi,结果将传递到.then(fetchApi2, CustomErrorHandling)该行。当then检测到错误/拒绝时,fetchApi2它不会触发,而是触发customErrorHandling

catch即使有了拒绝回调,保留它仍然是一种好做法。

更多解决、拒绝、捕获示例

已解决的示例:

let successFoo = new Promise((resolve, reject) => {resolve('foo')})
  .then(val => console.log(`I am resolved ${val}`), err => console.log(`I am rejected ${err}`))
  .catch(err => console.log("HELLO ERROR"))
// I am resolved foo
Enter fullscreen mode Exit fullscreen mode

被拒绝的示例:

let rejectFoo = new Promise((resolve, reject) => {reject('error foo')})
  .then(val => console.log(`I am resolved ${val}`), err => console.log(`I am rejected ${err}`))
  .catch(err => console.log("HELLO ERROR"))
// I am rejected error foo
Enter fullscreen mode Exit fullscreen mode

注意,它永远不会执行到 catch 语句块catch。第二个参数处理了这个问题。如果你想执行到 catch 语句块,只需不传递第二个参数即可:

let catchFoo = new Promise((resolve, reject) => {reject('error foo')})
  .then(val => console.log(`I am resolved ${val}`)).catch(err => console.log("HELLO ERROR"))
// HELLO ERROR
Enter fullscreen mode Exit fullscreen mode

好了,就这些!显然,这里只涵盖了基本情况,不可能面面俱到。我的目标不是把你们都培养成 Promise 专家,而是让你们掌握足够的知识入门,以便将来能够进行更高级的操作。希望这些内容对你们有所帮助!

Promise 还有更多未提及的内容,我建议你查阅一下all()finally()race()。我保证(😎),绝对值得你花时间!

感谢阅读,和往常一样,如果您发现任何错误/拼写错误/其他疏漏,请随时告诉我。

祝你编程愉快!

资源/更多阅读材料

文章来源:https://dev.to/iggredible/javascript-promise-101-3idl