文章目录
(资料图片仅供参考)
1. 简单运用Promise2. Promise三个状态3. Promise链式调用4. Promise.all5. Promise.race6. Axios——Promise封装的Ajax7. async/await
1. 简单运用Promise
function sleep(time) {return new Promise(function(resolve, reject) {if(time < 1000) {setTimeout(resolve("You are success"), time); } else {reject("fail"); } })}sleep(500).then(msg => {console.log(msg);}).catch(err => {console.log(err);});
控制台查看结果:
一般在使用Promise的时候,会在定义的函数中返回一个Promise构造函数,该构造函数包含两个参数,分别是resolve和reject。在上面的代码中,我们调用sleep方法,传入500。而我们使用了Promise的内置方法then,并且向then中传两个参数,一个是成功回调函数,一个是失败回调函数(本例中省略第二个参数)。当承诺成功兑现,第一个回调就会被调用,而当出现错误的时候,就会调用第二个参数reject(此处省略)。由于500小于1000,因此会执行setTimeout(resolve(“You are success”), time),输出了You are success。 在上面的例子中,我们还使用了Promise中的catch方法,当传入的参数大于500,则会执行catch方法,catch方法中传递一个参数,也就是reject,因此在失败的时候会执行它。如下:
2. Promise三个状态
promise对象用于作为异步任务结果的占位符,代表着一个我们暂时还没获得但未来有希望获得的值。正是因为这个原因,js的工程师们为Promise设置了三种状态,分别是等待态(pending),接受态(fullfilled),失败态(rejected)。当resolve函数被调用,promise的状态就会变成接受态;反之,如果reject方法被调用,或者在promise调用过程中发生了一个未处理的异常,则会进入失败态。一旦状态改变一次,promise将不会再次改变状态。
3. Promise链式调用
在本文开头,我们提到了回调地狱,最为常见的就是使用Ajax时,由于多个相互关联的请求多层嵌套,造成语法臃肿,对于后续的维护产生了不好的影响。而promise的链式调用,一定程度上解决了这个问题。我们再回到代码:
sleep(500).then(msg => {console.log(msg);}).catch(err => {console.log(err);});
此处使用了then方法,当promise成功兑现,则触发回调函数。我们还会发现,除了打印出You are success之外,还返回了一个Promise对象。
这就是说,每次then()都会返回一个新的promise,而这一个promise则可以交给下一个then方法使用。如下所示,每次then方法之后都返回了sleep(),而我们给出的sleep方法就是返回一个promise实例化对象,这样不断交给下一个then方法执行,就是我们的链式调用。
sleep(500).then(msg => {console.log(msg);return sleep(500);}).then(msg => {console.log(msg);return sleep(500);}).then(msg => {console.log(msg);return sleep(500);}).then(msg => {console.log(msg);return sleep(1500);}).catch(err => {console.log(err);});
控制台输出:
4. Promise.all
Promise除了链式调用实现多个步骤相互依赖之外,还提供了同时等待多个异步任务的方法,这里使用Promise中的all。
Promise.all([ sleep(500), sleep(600), sleep(1500)]).then(result => {console.log(result);}).catch(err => {console.log(err);})
控制台输出如下:
在这个方法的使用中,我们不关系任务执行的顺序。使用的时候,我们传入一个数组,元素则是promise对象。当调用all的时候,会等待所有的promise对象被兑现之后,返回一个成功值数组。 但是,当其中一个promise失败,则会影响整个结果,如下所示:
5. Promise.race
当我们只关心第一个返回结果的promise时,Promise.race能帮你达成这个愿望。如同Promise.all一样,我们在使用race的时候会传入一个promise对象数组,,一旦该数组中的一个promise被兑现,则返回结果。为了印证这一点,我们改造一下sleep方法,如下:
function sleep(time) {return new Promise(function(resolve, reject) {if(time < 1000) {setTimeout(resolve(`${time}s later...`), time); } else {reject("fail"); } })}Promise.race([ sleep(500), sleep(600), sleep(700)]).then(result => {console.log(result);})
控制台输出如下:
6. Axios——Promise封装的Ajax
在前端开发中,异步向后端发起请求有很多中方法,jQuery的Ajax,fetch,以及axios。axios的官网(http://www.axios-js.com/zh-cn/docs/)上是这样介绍的。 和jQuery的Ajax功能相似,都是对XMLHttpRequest的封装,让我们能够更好地进行异步请求。在Axios官网中还提到:“使用Promise管理异步,告别传统callback方式”,这里说的就是Promise的链式调用。正是这种语法的使用,让回调地狱问题得到了很好的解决。axios本质上也是对原生XHR的封装,只不过它是Promise的实现版本。 Axios简单使用代码如下:
axios.get(url) .then(function (response) {console.log(response); }) .catch(function (error) {console.log(error); });
本质上Axios的实例化对象就是一个promise,因此当我们熟悉了promise之后再来使用axios会比较容易上手。 为了加深理解,这里使用Promise封装一个XMLHttpRequest,模拟axios。代码如下:
function getJSON (url) {return new Promise( (resolve, reject) => {var xhr = new XMLHttpRequest() xhr.open("GET", url, true) xhr.onreadystatechange = () => {if (this.readyState === 4) {if (this.status === 200) {resolve(this.responseText) } else {var resJson = {code: this.status, response: this.response } reject(resJson, this) } } } xhr.send() })}getJSON ("https://blog.csdn.net/").then(res => {console.log(res); }).catch(error => {console.error(error);});
非常遗憾,出现了跨域问题,但是没关系,主要是通过一个实际的例子让帮助大家更进一步理解promise。
7. async/await
不知道大家发现没有,Promise虽然一定程度上解决了回调地狱问题,但是链式调用的语法仍然不够优雅,我们如果能用完全同步的语法来解决异步问题,那该有多好。在ES6中,我们可以使用生成器和promise相结合的方式实现这个愿望。而JS伟大的工程师们为我们提出了更好的方案,async/await,这号称是JavaScript解决异步问题的终极解决方案,其实是对Promise的再一次封装,也可以说是语法糖。那么它到底如何,就请听下回分解吧!