本教程涵盖了JavaScript Promise的基础知识,展示了如何在JavaScript开发中利用它们。
Promise的概念对于Web开发而言并不是新事物。 我们中的许多人已经以诸如Q,when.js,RSVP.js之类的库的形式使用了Promise。甚至jQuery都有一个称为Deferred对象的东西,它类似于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()
实现诺言。 发生错误时,将使用Error
对象调用reject()
。 这表明诺言被拒绝了。
现在,让我们构建一些简单的方法来显示如何使用promise。 以下代码向Web服务发出异步请求,该请求以JSON格式返回随机笑话。 让我们研究一下如何使用诺言:
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()
。 发生任何错误时,将使用Error
对象调用reject()
。
实例化Promise
对象时,我们获得了将来将可用的数据的代理。 就我们而言,我们希望将来某个时候从远程服务返回一些数据。 那么,我们如何知道数据何时可用? 这是使用Promise.then()
函数的地方。 该函数有两个参数:成功回调和失败回调。 在Promise
结算(即实现或拒绝)时调用这些回调。 如果实现了诺言,则将使用传递给resolve()
的实际数据触发成功回调。 如果承诺被拒绝,则将调用失败回调。 您传递给reject()
都将作为参数传递给此回调。
试试这个CodePen示例。 要查看新的随机笑话,请点击嵌入右下角的RERUN按钮。 另外,打开浏览器控制台,以便您可以看到执行代码不同部分的顺序。
请参阅PenP上的SitePoint ( @SitePoint ) 的JavaScript承诺概述 。
请注意,promise可以具有三种状态:
- 待处理(未实现或拒绝)
- 完成
- 被拒绝
Promise.status
属性是代码不可访问的私有属性,它提供有关这些状态的信息。 一旦诺言被拒绝或实现,此状态便会与之永久关联。 这意味着一个承诺只能成功或失败一次。 如果Promise已被兑现,然后您通过两个回调将then()
附加到它上,则将成功调用成功回调。 因此,在承诺的世界中,我们对知道何时兑现承诺不感兴趣。 我们只关心承诺的最终结果。
连锁承诺
有时希望将承诺链接在一起。 例如,您可能要执行多个异步操作。 当一个操作为您提供数据时,您将开始对该数据块执行其他操作,依此类推。 可以将承诺链接在一起,如以下示例所示:
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()
。 但是,如果您在then()
内部返回一个诺言,则下一个then()
在其上等待并在该诺言得到解决时被调用。
处理错误
您已经知道then()
函数将两个回调作为参数。 如果诺言被拒绝,将调用第二个。 但是我们还有一个catch()
函数,可用于处理承诺拒绝。 看下面的代码:
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()
没有失败回调,则控件将通过失败回调或下一个catch()
前进到下一个then()
catch()
。 除了显式的promise拒绝之外,当从Promise
构造函数回调中引发任何异常时,还会调用catch()
。 因此,您也可以将catch()
用于记录目的。 请注意,我们可以使用try...catch
来处理错误,但是对于promises来说并不是必须的,因为任何异步或同步错误总是由catch()
。
结论
这只是JavaScript的新Promises API的简要介绍。 显然,它使我们可以非常轻松地编写异步代码。 我们可以像往常一样进行操作,而无需知道将来异步代码将返回什么值。 关于API的更多内容未在此处介绍。 要了解有关Promises的更多信息,请查看我的后续文章A Deep Dive Into JavaScript Promises以及以下重要资源:
From: https://www.sitepoint.com/overview-javascript-promises/