本教程涵盖了 JavaScript Promise 的基础知识,展示了如何在 JavaScript 开发中利用它们。
承诺的概念对于 Web 开发来说并不新鲜。我们中的许多人已经以库的形式使用过 Promise,例如 Q、when.js、RSVP.js 等。甚至 jQuery 也有一个叫做Deferred object的东西,它类似于 Promise。但是现在我们在 JavaScript 中对 Promise 提供了原生支持,这真的很令人兴奋。
概述
Promise
对象表示可能尚不可用但将在未来某个时间解决的值。它允许您以更同步的方式编写异步代码。例如,如果您使用 Promise API 对远程 Web 服务进行异步调用,您将创建一个Promise
对象,该对象代表该 Web 服务将来返回的数据。需要注意的是,实际数据尚不可用。当请求完成并且从 Web 服务返回响应时,它将变为可用。同时,Promise
对象充当实际数据的代理。此外,您可以将回调附加到Promise
对象,一旦实际数据可用,就会调用该回调。
API
首先,让我们检查以下代码,它创建了一个新Promise
对象:
const promise = new Promise((resolve, reject) => {
//asynchronous code goes here
});
我们首先实例化一个新Promise
对象并传递一个回调函数。回调有两个参数,resolve
和reject
,它们都是函数。您所有的异步代码都在该回调中。如果一切顺利,则通过调用来履行承诺resolve()
。如果发生错误,reject()
则使用Error
对象调用。这表明承诺被拒绝。
现在让我们构建一些简单的东西来展示如何使用 Promise。以下代码向 Web 服务发出异步请求,该请求返回 JSON 格式的随机笑话。让我们看看这里是如何使用 Promise 的:
const promise = new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open('GET', 'https://api.icndb.com/jokes/random');
request.onload = () => {
if (request.status === 200) {
resolve(request.response); // we got data here, so resolve the Promise
} else {
reject(Error(request.statusText)); // status is not 200 OK, so reject
}
};
request.onerror = () => {
reject(Error('Error fetching data.')); // error occurred, reject the Promise
};
request.send(); // send the request
});
console.log('Asynchronous request made.');
promise.then((data) => {
console.log('Got data! Promise fulfilled.');
document.body.textContent = JSON.parse(data).value.joke;
}, (error) => {
console.log('Promise rejected.');
console.log(error.message);
});
在前面的代码中,Promise
构造函数回调包含用于从远程服务获取数据的异步代码。在这里,我们只是向https://api.icndb.com/jokes/random创建了一个 Ajax 请求,它返回一个随机笑话。当从远程服务器接收到 JSON 响应时,它会传递给resolve()
. 如果出现任何错误,reject()
则使用Error
对象调用。
当我们实例化一个Promise
对象时,我们会得到一个将来可用的数据的代理。在我们的例子中,我们期望在未来的某个时间点从远程服务返回一些数据。那么,我们如何知道数据何时可用?这是Promise.then()
使用该功能的地方。这个函数有两个参数:一个成功回调和一个失败回调。这些回调在Promise
解决(即完成或拒绝)时调用。如果 promise 被实现,成功回调将使用您传递给的实际数据触发resolve()
。如果 promise 被拒绝,将调用失败回调。无论您传递给什么,reject()
都将作为参数传递给此回调。
试试这个CodePen示例。要查看新的随机笑话,请点击嵌入右下角的RERUN按钮。此外,打开浏览器控制台,以便查看代码不同部分的执行顺序。
请注意,promise 可以具有三种状态:
- 待处理(未完成或拒绝)
- 完成
- 拒绝
该Promise.status
属性是代码不可访问且私有的,提供有关这些状态的信息。一旦一个承诺被拒绝或履行,这个状态就会永久地与之关联。这意味着一个承诺只能成功或失败一次。如果promise 已经实现,然后你then()
用两个回调将a 附加到它,那么成功回调将被正确调用。因此,在 Promise 的世界中,我们对知道 Promise 何时完成并不感兴趣。我们只关心承诺的最终结果。
链接承诺
有时需要将 Promise 链接在一起。例如,您可能需要执行多个异步操作。当一个操作为您提供数据时,您将开始对该数据块执行其他操作,依此类推。Promise 可以链接在一起,如以下示例所示:
function getPromise(url) {
// return a Promise here
// send an async request to the url as a part of promise
// after getting the result, resolve the promise with it
}
const promise = getPromise('some url here');
promise.then((result) => {
//we have our result here
return getPromise(result); //return a promise here again
}).then((result) => {
//handle the final result
});
棘手的部分是,当您在 内部返回一个简单值时,将使用该返回值调用then()
下一个。then()
但是如果你在里面返回一个promise then()
,nextthen()
会等待它并在promise 完成时被调用。
处理错误
您已经知道该then()
函数将两个回调作为参数。如果 promise 被拒绝,将调用第二个。但是我们也有一个catch()
函数,可以用来处理 promise 的拒绝。看看下面的代码:
promise.then((result) => {
console.log('Got data!', result);
}).catch((error) => {
console.log('Error occurred!', error);
});
这相当于:
promise.then((result) => {
console.log('Got data!', result);
}).then(undefined, (error) => {A
console.log('Error occurred!', erroAr);
});
请注意,如果 promise 被拒绝并且then()
没有失败回调,则控件将前进到then()
带有失败回调或 next 的下一个catch()
。除了显式的 promise 拒绝之外,当构造函数回调catch()
抛出任何异常时也会调用。Promise
因此,您也可以catch()
用于记录目的。请注意,我们可以使用它try...catch
来处理错误,但对于 Promise,这不是必需的,因为任何异步或同步错误总是被catch()
.
结论
这只是对 JavaScript 的新 Promises API 的简要介绍。很明显,它让我们可以非常轻松地编写异步代码。我们可以照常进行,而不知道将来异步代码会返回什么值。API 的更多内容未在此处介绍。