- 原文地址:JavaScript, Asynchronous Programming, and Promises
- 原文作者:Mark Sanders
- 译文出自:掘金翻译计划
在这篇教程中你将学习什么是 JS 中的 Promises,Promises 可以有几种状态,以及如何处理在 Promises 中的异步错误。
在这篇教程中你将学习什么是 JS 中的 Promises,Promises 可以有几种状态,以及如何处理在 Promises 中的异步错误。
如果你已经熟悉这个主题,你可能会对 如何处理在 JavaScript 中的 Promise 申明 感兴趣。
介绍
现在,你只有一些常规值来处理。你在创建了一个变量或者常量后保存了一些东西,变量或常量就可以立即使用了。例如,你可以在控制台打印它。
但是如果这个值没有立即出现,或者需要一些时间出现呢?我们经常从数据库或者是外部服务中获取数据。这些操作会有些耗时,下面是两种处理方式:
- 我们可以尝试中断程序的执行,直到我们接收到了数据。
- 我们也可以继续执行,当数据呈现的时候再去处理。
这不是说一种方法肯定比另外一种好。两者都契合了不同的需求,因为在不同的情况下我们需要不同的行为。
如果你正在等待的数据对继续执行来说是至关重要的,那么你需要中断执行并且你无法绕开它。如果你可以推迟这个过程,当然就不值得浪费时间,因为你可以做一些别的事情。
JavaScript 中的 Promise 到底是什么
Promise 是一个特殊的对象类型,它可以帮助你处理一些异步操作。
在不能立即获得返回值的情况下,许多函数会给你返回一个 Promise。
const userCount = getUserCount();
console.log(userCount); // Promise {<pending>}
在这个例子中,getUserCount
是一个返回 Promise
的函数。如果我们尝试立即显示 userCount
变量的值,我们会得到的是 Promise {<pending>}
。
这是有可能发生的,因为还没有数据,我们需要等待它。
在 JavaScript 中 Promise 的状态
一个 Promise 可以有多种状态:
- 进行中(Pending ) - 响应还没有就绪。请等待。
- 已完成(Fulfilled) - 响应已完成。成功。请获取数据。
- 已拒绝(Rejected) - 出现了一个错误。请处理它。
当在进行中的状态时,我们不可以做任何有用的事情,仅仅是等待。在其他状态中,我们可以增加处理函数,当一个 Promise 进入 Fulfilled 或 Rejected 状态时将调用这些函数。
为了处理这个成功接收的数据,我们需要一个 then
函数。
const userCount = getUserCount();
const handleSuccess = (result) => {
console.log(`Promise was fulfilled. Result is ${result}`);
}
userCount.then(handleSuccess);
对于错误处理我们使用 catch
。
const handleReject = (error) => {
console.log(`Promise was rejected. The error is ${error}`);
}
userCount.catch(handleReject);
请注意 getUserCount
函数返回一个 Promise,所以我们不能直接使用 userCount
。为了有效处理返回的数据(data),我们需要增加 then
和 catch
的处理函数,以便成功或失败的时候可以被调用。
then
和 catch
函数可以被连续的调用。在这个例子中,我们将会同时关注成功(success)和失败(failure)。
const userCount = getUserCount();
const handleSuccess = (result) => {
console.log(`Promise was fulfilled. Result is ${result}`);
}
const handleReject = (error) => {
console.log(`Promise was rejected. The error is ${error}`);
}
userCount.then(handleSuccess).catch(handleReject);
在 JS Promises 中的错误处理
假设我们有一个 getUserData(userId)
函数,它返回关于用户的信息或者抛出一个错误如果 userId
参数有问题的话。
以前,我们添加常规的 try
/catch
并且在 catch 块中处理错误。
try {
console.log(getUserData(userId));
} catch (e) {
handleError(e);
}
但是使用常规的 try
/catch
不能在 Promise 的异步代码中捕获出现的错误。
让我们尝试使用返回一个 Promise 的异步函数 fetchUserData(userId)
来替代直接返回结果的同步函数 getUserData(userId)
。
我们想要保持行为的一致性——如果成功则显示结果,如果失败则处理错误。
try {
fetchUserData(userId).then(console.log);
} catch (e) {
handleError(e);
}
但是我们不会成功。这在同步代码中不会有问题,所以执行将会被继续。但在异步代码中出现错误时,我们将收到一个 UnhandledPromiseRejection
,并且我们的程序将会终止。
为了更好的理解程序执行的顺序,让我们来增加一个 finally
的块。它将总是会运行(正如预期的那样),但是它会运行在 UnhandledPromiseRejection
之前还是之后呢?
try {
fetchUserData(userId).then(console.log);
} catch (e) {
handleError(e);
} finally {
console.log('finally');
}
让我们一起按步骤来尝试:
- 在
try
块中我们调用了一个fetchUserData
函数,它将在Pending
状态返回一个Promise
。 - 这个
catch
块会被忽视,因为在try
块中没有错误。异步执行还没有运行! finally
行显示在屏幕上。- 在异步代码中出现了一个错误并且我们在控制台上看见了错误信息——
UnhandledPromiseRejectionWarning
。
为了避免 Promises 中出现未处理的 Rejections,你应该总是在 catch
中处理它们。
fetchUserJavaScriptData(userId).then(console.log).catch(handleError);
这样的代码会变得更加简短、清晰,并且避免了破坏代码的意外错误。
这里有一个关于处理错误的有趣问题在 JS Promises from a Junior Interview。