理解 JavaScript 中的 Promises – JavaScript 完全手册(2018版)
栏目: JavaScript · 发布时间: 5年前
内容简介:小编推荐:
小编推荐: 掘金是一个面向 程序员 的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。
注:本文为 《 JavaScript 完全手册(2018版) 》第25节,你可以查看该手册的完整目录。
Promises 是一种 JavaScript 中处理异步代码的方法,无需在代码中编写太多回调。
Promises 通常定义为最终可用的值的代理(a proxy for a value that will eventually become available)。
Promises 是处理异步代码的一种方法,无需在代码中编写太多回调。
其实 Promises 已经存在多年,但是直到 ES2015 才被标准化和引入,现在它们已经在 ES2017 中被 async(异步) 函数所取代。
Async functions(异步函数) 使用 promises API 作为构建块,因此理解 Promises 是必须的,即使在较新的代码中,你可能会使用 async(异步) 函数而不是promises 。
简而言之,承诺是如何工作的
一旦调用了一个 promise ,它就会以 pending(挂起) 状态 开始。 这意味着调用者函数继续执行,同时等待 promise 执行自己的处理,并为调用者函数提供一些反馈。
此时,调用者函数等待它以 resolved
状态或者 rejected
状态 返回 promise,但是如果你知道 JavaScript 是异步的,那么 函数会在 promise 完成其工作时继续执行 。
哪些 JS API使用 promises ?
除了你自己的代码和库代码之外,Promises 还被用于标准的现代 Web API,例如:
- Battery API
- Fetch API
- Service Workers
在现代 JavaScript 中你不太可能发现自己没有使用 Promises ,所以让我们开始深入了解它们。
创建 promise
Promise API 公开了一个 Promise 构造函数,你可以使用 new Promise()
进行初始化:
let done = true const isItDoneYet = new Promise( (resolve, reject) => { if (done) { const workDone = 'Here is the thing I built' resolve(workDone) } else { const why = 'Still working on something else' reject(why) } } )
正如你所看到的,promise 会检查 done
这个全局常量,如果 done
为 true
,我们将返回 resolved
状态的 promise ,否则将返回 rejected
状态的 promise 。
使用 resolve
和 reject
时,我们可以回传一个值,在上面的例子中我们只是回传了一个字符串,但它也可以是一个对象。
使用 promise
在上一节中,我们介绍了如何创建 promise 。
现在让我们看看如何使用 promise 。
const isItDoneYet = new Promise( //... ) const checkIfItsDone = () => { isItDoneYet .then((ok) => { console.log(ok) }) .catch((err) => { console.error(err) }) }
运行 checkIfItsDone()
将执行 isItDoneYet()
promise 并使用 then
回调等待该 promise 的 resolve
状态。如果有错误,它将在 catch
回调中处理这个错误。
链式调用 promises
promise 可以返回另一个 promise,形成一个链式 promise。
链式调用 promises 的一个很好的例子是 Fetch API,它是XMLHttpRequest API 上层 API,我们可以使用它来获取资源,并在获取资源时对 Promise 链进行排列。
Fetch API 基于 promise 机制,调用 fetch()
等同于我们通过 new promise()
定义一个我们自己的 promise。
链式调用 promises 例子
const status = (response) => { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response) } return Promise.reject(new Error(response.statusText)) } const json = (response) => response.json() fetch('/todos.json') .then(status) .then(json) .then((data) => { console.log('Request succeeded with JSON response', data) }) .catch((error) => { console.log('Request failed', error) })
在这个例子中,我们调用 fetch()
从根域名中的 todos.json
文件获取一个 TODO 清单,我们创建了一个 promises 链。
运行 fetch()
返回一个包含很多个属性的 response ,我们引用了其中的:
-
status
,数字值表示的 HTTP 状态代码 -
statusText
,状态消息,请求成功时为OK
response
还有一个 json()
方法,该方法返回一个 resolve
状态的 promise,并且将响应内容转化为 JSON 作为该 promise 的回传值。
在这些前提下,会发生这样的情况:链中的第一个 promise 是我们定义的函数 status()
,它检查响应状态,如果响应不成功(在200到299之间),则 reject
该 promise 。
此操作将导致 promise 链跳过队列中的所有的 promise ,并将直接跳到底部的 catch()
语句,打印 Request failed
文本以及错误消息。
如果成功,它会调用我们定义的 json()
函数。 前一个 promise 返回 response 对象,作为第二个 promise 的输入。
在这种情况下,我们返回处理过的 JSON 数据 ,因此第三个 promise 直接接收 JSON:
.then((data) => { console.log('Request succeeded with JSON response', data) })
在控制台会打印出这些内容。
处理错误
在上面的例子中,promises 链后面有一个额外的 catch 块。当 promises 链有任何错误发生,将 promise 置为 reject
,控制会转到链中最近的 catch()
语句。
new Promise((resolve, reject) => { throw new Error('Error') }) .catch((err) => { console.error(err) }) // or new Promise((resolve, reject) => { reject('Error') }) .catch((err) => { console.error(err) })
级联错误
如果在 catch()
内部又抛出一个错误,你可以添加第二个 catch()
处理它,以此类推。
new Promise((resolve, reject) => { throw new Error('Error') }) .catch((err) => { throw new Error('Error') }) .catch((err) => { console.error(err) })
Promise.prototype.finally()
这是 ES2018(ES9)的新特性.
当一个 promise 得到满足(fulfilled)时,它会一个接一个地调用 then()
方法。如果在此期间发生错误,则跳过 then()
方法并执行 catch()
方法。
finally()
允许您运行一些代码,无论 promise 的执行成功或失败:
fetch('file.json') .then(data => data.json()) .catch(error => console.error(error)) .finally(() => console.log('finished'))
协调 promises
Promise.all()
如果你需要同步处理多个 promises, Promise.all()
可以帮助你定义一组 promises 列表,等待它们全部 resolved 之后再执行某些操作。比如:
const f1 = fetch('/something.json') const f2 = fetch('/something2.json') Promise.all([f1, f2]).then((res) => { console.log('Array of results', res) }) .catch((err) => { console.error(err) })
ES2015 的解构语法也允许你这么做:
Promise.all([f1, f2]).then(([res1, res2]) => { console.log('Results', res1, res2) })
这不仅限于 fetch,任何 promise 都可以处理。
Promise.race()
Promise.race()
会在你传递给它的第一个 promise resolves 时运行,并且它只运行一次附加的回调,并传入首先 resolved 的 promise 返回的结果。
示例:
const first = new Promise((resolve, reject) => { setTimeout(resolve, 500, 'first') }) const second = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'second') }) Promise.race([first, second]).then((result) => { console.log(result) // second })
常见错误
Uncaught TypeError: undefined is not a promise
如果你控制台中看到 Uncaught TypeError: undefined is not a promise
错误,请确保使用的是 new Promise()
而不是 Promise()
。
如果你还有什么疑问,可以查看ES6 Promise 指南
如果你觉得本文对你有帮助,那就请分享给更多的朋友
关注「前端干货精选」加星星,每天都能获取前端干货
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- MySQL™ 参考手册(关于本手册)
- LLVM 程序员手册 —— LLVM 4.0 文档(非常非常完整的手册)
- [译]Python手册——模块
- Axure函数使用手册
- 线上故障处理手册
- OpenSSH 实践手册
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。