Promise是ES6原生提供的一个用来传递异步消息的对象。它减少了传统ajax金字塔回调,可以将异步操作以同步操作的流程表达出来使得代码维护和可读性方面好很多。
Promise的状态:
既然是用来传递异步消息的那肯定就会有异步消息的状态;所以promise提供了3种状态:pending(进行中),resolved(已完成或者称 fulfilled),rejected(失败)。不同的是Promise不会受外界影响,只有异步操作结果才能决定当前是哪种状态,任何其他非异步操作都不能改变。所以当状态发生改变了,即一个异步操作完成了就不会再变了。所以Promise对象的状态改变只有两种可能:①从pending---->resolved;②从pending---->rejected。只要这两种情况中的其中一种完成了就不会再发生改变了。即使对Promise对象添加回调函数也会得到这个结果。这里就跟事件Event不同了,Event是错过了这次监听,再去监听的时候是得不到结果的。
虽然Promise使得异步操作流程变得简单,但有时候它的优点也是它的缺点。比如当Promise建立时就会立即执行进入pending状态因为无法当前状态所以就无法取消Promise,只有异步操作才能改变。而且如果不设置回调函数的话Promise内部报错是不会反应到外部的。
在执行new Promise时浏览器会同步执行Promise构造函数中的方法,初始状态为pending,并从这个初始状态到resolved(fulfilled)或rejected状态的转换。一旦达到这两个其中之一的状态promise的状态就稳定了,是最终状态无法转换。
根据Promise规范,then或catch即使未显式指定返回值,它们也总是默认返回一个新的fulfilled状态的promise对象。
上一小段代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>ES6 Promise</title> 6 </head> 7 <body> 8 <script> 9 function asyncFn() { 10 return new Promise((resolve, reject) => { 11 setTimeout(() => { 12 console.log("执行异步操作。。。。。"); 13 resolve("执行异步操作成功将数据通过resolve方法返回"); 14 }, 1000); 15 }); 16 } 17 asyncFn().then( data => { 18 console.log(data); //得到的就是Promise中resolve方法传递过来的数据 19 }); 20 </script> 21 </body> 22 </html>
我们可以看到代码可以正常执行:
这里demo代码中Promise构造函数接受一个函数作为参数,其中该函数分别接收resolve和reject这两个参数。如果Promise中异步操作成功,则resolve方法将Promise对象状态从pending变成resolved。如果失败则将pending变成rejected。
Promise API--> all、race:
all方法:
all方法接收一个数组参数,里面的值最终都返回Promise对象。数组里面的异步操作都是并行执行的,当数组里的异步操作都执行完了才会进入到then中并将数组中的异步操作的数据返回。
all方法特别适合处理依赖多个异步请求的场景。当最后一个异步请求处理完成时返回所有异步请求结果
demo代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>ES6 Promise</title> 6 </head> 7 <body> 8 <script> 9 function asyncFn1() { 10 return new Promise((resolve, reject) => { 11 setTimeout(() => { 12 console.log("执行异步操作asyncFn1。。。。。"); 13 resolve("执行异步操作asyncFn1成功将数据通过resolve方法返回"); 14 }, 1000); 15 }); 16 } 17 function asyncFn2() { 18 return new Promise((resolve, reject) => { 19 setTimeout(() => { 20 console.log("执行异步操作asyncFn2。。。。。"); 21 resolve("执行异步操作asyncFn2成功将数据通过resolve方法返回"); 22 }, 1000); 23 }); 24 } 25 function asyncFn3() { 26 return new Promise((resolve, reject) => { 27 setTimeout(() => { 28 console.log("执行异步操作asyncFn3。。。。。"); 29 resolve("执行异步操作asyncFn3成功将数据通过resolve方法返回"); 30 }, 1000); 31 }); 32 } 33 Promise.all([asyncFn1(),asyncFn2(),asyncFn3()]).then( data => { 34 console.log(data); //得到的就是数组中的异步操作的数据 35 }); 36 37 </script> 38 </body> 39 </html>
race方法:
race方法接收的也是一个数组参数,数组中元素也是Promise对象;返回的是执行最快的那个异步操作,其实从字面上的意思我们也可以看出来race就是赛跑的意思。
race适合多个异步请求中取最快那个异步请求场景;同时发送多个异步请求,只要一个请求成功那么就以该Promise做为最终状态并返回其值。其中对于状态稳定的Promise(fulfilled或rejected状态),哪个排第一,将返回哪个。
demo代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>ES6 Promise</title> 6 </head> 7 <body> 8 <script> 9 function asyncFn1() { 10 return new Promise((resolve, reject) => { 11 setTimeout(() => { 12 console.log("执行异步操作asyncFn1。。。。。"); 13 resolve("执行异步操作asyncFn1成功将数据通过resolve方法返回"); 14 }, 1000); 15 }); 16 } 17 function asyncFn2() { 18 return new Promise((resolve, reject) => { 19 setTimeout(() => { 20 console.log("执行异步操作asyncFn2。。。。。"); 21 resolve("执行异步操作asyncFn2成功将数据通过resolve方法返回"); 22 }, 2000); 23 }); 24 } 25 function asyncFn3() { 26 return new Promise((resolve, reject) => { 27 setTimeout(() => { 28 console.log("执行异步操作asyncFn3。。。。。"); 29 resolve("执行异步操作asyncFn3成功将数据通过resolve方法返回"); 30 }, 3000); 31 }); 32 } 33 Promise.race([asyncFn1(),asyncFn2(),asyncFn3()]).then( data => { 34 console.log(data); //得到的就是Promise中resolve方法传递过来的数据 35 }); 36 37 </script> 38 </body> 39 </html>
这里只返回了最快的那个异步操作数据,但是asyncFn2和asyncFn3并没有停止是因为在数组中异步操作是并行的而且Promise构造函数执行的时候会先执行构造函数中的操作。
Promise原型的方法:
Promise原型只有Promise.prototype.then和Promise.prototype.catch。then方法让我们可以非常方便的使用链式调用;同时then可以接受两个回调,第一个是处理成功的回调,第二个是处理失败的回调。
demo如下:
1 function asyncFn4() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 console.log("执行异步操作asyncFn4。。。。。"); 5 resolve("执行异步操作asyncFn4成功将数据通过resolve方法返回"); 6 }, 3000); 7 }); 8 } 9 asyncFn4().then(res => { 10 console.log("处理成功的回调......"); 11 console.log(res); 12}, rejected => { 13 console.log("处理失败的回调。。。" + rejected); 14 });
catch用于捕获异常,无论是抛出的异常还是reject掉的异常都会被catch捕获。
我们对上面的代码做点小改动:
1 function asyncFn4() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 console.log("执行异步操作asyncFn4。。。。。"); 5 resolve("执行异步操作asyncFn4成功将数据通过resolve方法返回"); 6 }, 3000); 7 }); 8 } 9 asyncFn4().then(res => { 10 console.log("处理成功的回调......"); 11 console.log(res); 12 }, rejected => { 13 console.log("处理失败的回调。。。" + rejected); 14 }).catch(err => { 15 console.log('catch到的异常,无论是Promise内部报错还是reject掉的异常都会被catch捕获') 16 });
then语句的onRejected回调并不能捕获onFulfilled回调内抛出的错误,其后的catch却可以捕获抛出的错误回调。