JavaScript Promise 101
什么是Promise?
使用new Promise
Promise + setTimeout
连续链
API 调用
捕获错误和处理拒绝
不同的拒绝方法
更多解决、拒绝、捕获示例
资源/更多阅读材料
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
了解PromiseJavaScript 的工作原理将极大地提升你的开发技能。以下我将分享:
- 基本
Promise - 如何使用
then 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
我们可以使用简写方式来简化它Promise.resolve。以下与上述等效:
let foo = Promise.resolve('foo')
foo.then(value => console.log(value)) // foo
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
注意对数的顺序。
一些说明:
- 一旦声明了承诺(
new Promise(...)),时间就开始倒计时。 promise1它本身就是一个 Promise 对象。你可以在控制台中看到它:promise1 // Promise {<resolved>: "foo"}- 你可以使用
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
你会注意到,在执行完 `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
链继续,但 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))
当我们第一次使用 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"}
养成及时发现 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
刚才发生了什么?
让我们比较一下,如果我们只放入then“无”这个选项会是什么情况。catch
foo = new Promise((resolve, reject) => {reject('error foo')})
foo.then(val => console.log(val))
// Promise {<rejected>: "error foo"}
啊,我的 Chrome 控制台报错了,提示有个错误没有被捕获。我们需要捕获这个错误。让我们来捕获它!
foo.then(val => console.log(val)).catch(err => console.log(err)) // error foo
现在我们看到更清晰的日志了!
不同的拒绝方法
你可能会问:“嘿,伙计,如果我有一条项链怎么办?”
let promise1 = new Promise(fetchSomeApi);
promise
.then(processApi)
.then(fetchApi2)
.then(processApi2)
.catch(handleCommonError)
“我想对processApi剩余的错误做些不同的处理,让 handleCommonError 函数来处理这些错误?”
幸运的是,捕获错误的方法不止一种!then接受第二个参数。
回顾一下我们上面的第一段代码:let foo = new Promise((resolve, reject) => {resolve('foo')})。我们将使用reject它进行自定义错误处理。
你可以这样做:
promise
.then(processApi)
.then(fetchApi2, customErrorHandling)
.then(processApi2)
.catch(handleCommonError)
如果在执行过程中出现错误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
被拒绝的示例:
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
注意,它永远不会执行到 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
好了,就这些!显然,这里只涵盖了基本情况,不可能面面俱到。我的目标不是把你们都培养成 Promise 专家,而是让你们掌握足够的知识入门,以便将来能够进行更高级的操作。希望这些内容对你们有所帮助!
Promise 还有更多未提及的内容,我建议你查阅一下all(),finally()和race()。我保证(😎),绝对值得你花时间!
感谢阅读,和往常一样,如果您发现任何错误/拼写错误/其他疏漏,请随时告诉我。
祝你编程愉快!