十二、Promise的学习笔记(Promise的基本使用、链式编程、all())
一、認識Promise
ES6中一個非常重要和好用的特性就是Promise
- 但是初次接觸Promise會一臉懵逼,這TM是什么東西?
- 看看官方或者一些文章對它的介紹和用法,也是一頭霧水。
Promise到底是做什么的呢?
- Promise是異步編程的一種解決方案。
那什么時候我們會來處理異步事件呢?
- 一種很常見的場景應該就是網絡請求了。
- 我們封裝一個網絡請求的函數,因為不能立即拿到結果,所以不能像簡單的3+4=7一樣將結果返回。
- 所以往往我們會傳入另外一個函數,在數據請求成功時,將數據通過傳入的函數回調出去。
- 如果只是一個簡單的網絡請求,那么這種方案不會給我們帶來很大的麻煩。
但是,當網絡請求非常復雜時,就會出現回調地獄。
OK,我以一個非??鋸埖陌咐齺碚f明。
我們來考慮下面的場景(有夸張的成分):
- 我們需要通過一個url1從服務器加載一個數據data1,data1中包含了下一個請求的url2
- 我們需要通過data1取出url2,從服務器加載數據data2,data2中包含了下一個請求的url3
- 我們需要通過data2取出url3,從服務器加載數據data3,data3中包含了下一個請求的url4
- 發送網絡請求url4,獲取最終的數據data4
上面的代碼有什么問題嗎?
- 正常情況下,不會有什么問題,可以正常運行并且獲取我們想要的結果。
- 但是,這樣額代碼難看而且不容易維護。
我們更加期望的是一種更加優雅的方式來進行這種異步操作。
如何做呢?
- 就是使用Promise。
- Promise可以以一種非常優雅的方式來解決這個問題。
二、Promise的基本使用
2.1 定時器的異步事件
我們先來看看Promise最基本的語法。
這里,我們用一個定時器來模擬異步事件:
- 假設下面的data是從網絡上1秒后請求的數據
- console.log就是我們的處理方式。
上圖是我們過去的處理方式,我們將它換成Promise代碼:
這個例子會讓我們感覺脫褲放屁,多此一舉
- 首先,下面的Promise代碼明顯比上面的代碼看起來還要復雜。
- 其次,下面的Promise代碼中包含的resolve、reject、then、catch都是些什么東西?
我們先不管第一個復雜度的問題,因為這樣的一個屁大點的程序根本看不出來Promise真正的作用。
2.2 定時器異步事件解析
我們先來認認真真的讀一讀這個程序到底做了什么?
- new Promise很明顯是創建一個Promise對象
- 小括號中((resolve, reject) => {})也很明顯就是一個函數,而且我們這里用的是之前剛剛學習過的箭頭函數。
但是resolve, reject它們是什么呢?
- 我們先知道一個事實:在創建Promise時,傳入的這個箭頭函數是固定的(一般我們都會這樣寫)
- resolve和reject它們兩個也是函數,通常情況下,我們會根據請求數據的成功和失敗來決定調用哪一個。
成功還是失敗?
- 如果是成功的,那么通常我們會調用resolve(messsage),這個時候,我們后續的then會被回調。
- 如果是失敗的,那么通常我們會調用reject(error),這個時候,我們后續的catch會被回調。
OK,這就是Promise最基本的使用了。
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><script>// 1.使用setTimeout// setTimeout(() => {// console.log('Hello world!');// }, 1000)// new Promise(參數) 參數->函數(resolve,reject)// resolve,reject它們本身又是函數// 鏈式編程/*new Promise((resolve,reject) => {// 第一次發生網絡請求的代碼setTimeout(() => {// console.log('Hello world!');resolve()}, 1000)}).then(() => {// 第一次拿到結果后的處理代碼console.log('Hello world!');console.log('Hello world!');return new Promise((resolve, reject) => {// 第二次發生網絡請求的代碼setTimeout(() => {resolve()}, 1000)}).then(() => {// 第二次拿到結果后的處理代碼console.log('Hello vuejs!');console.log('Hello vuejs!');return new Promise((resolve, reject) => {// 第三次發生網絡請求的代碼setTimeout(() => {resolve()}, 1000)}).then(() => {// 第三次拿到結果后的處理代碼console.log('Hello promise!');console.log('Hello promise!');})})})*/// 什么情況下會用到Promise?// 一般情況下是有異步操作時,會使用Promise對這個異步操作進行封裝new Promise((resolve, reject) => {setTimeout(() => {// 成功的時候調用resolve// resolve('hello data')// 失敗的時候調用rejectreject('error message')}, 1000)}).then((data) => {console.log(data);console.log(data);console.log(data);console.log(data);}).catch((err) => {console.log(err);})</script> </body> </html>then()處理成功,catch()處理失敗:
不寫catch(),把處理成功和失敗的兩個函數都作為then()函數的參數傳進去:
三、Promise三種狀態
首先, 當我們開發中有異步操作時, 就可以給異步操作包裝一個Promise
異步操作之后會有三種狀態,我們一起來看一下這三種狀態:
四、Promise的鏈式編程
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <script>// 1.使用setTimeout// setTimeout(() => {// console.log('Hello world!');// }, 1000)// new Promise(參數) 參數->函數(resolve,reject)// resolve,reject它們本身又是函數// 鏈式編程new Promise((resolve,reject) => {// 第一次發生網絡請求的代碼setTimeout(() => {// console.log('Hello world!');resolve()}, 1000)}).then(() => {// 第一次拿到結果后的處理代碼console.log('Hello world!');console.log('Hello world!');return new Promise((resolve, reject) => {// 第二次發生網絡請求的代碼setTimeout(() => {resolve()}, 1000)}).then(() => {// 第二次拿到結果后的處理代碼console.log('Hello vuejs!');console.log('Hello vuejs!');return new Promise((resolve, reject) => {// 第三次發生網絡請求的代碼setTimeout(() => {resolve()}, 1000)}).then(() => {// 第三次拿到結果后的處理代碼console.log('Hello promise!');console.log('Hello promise!');})})}) </script> </body> </html> <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><script>// 網絡請求: aaa -> 處理代碼(10行代碼)// 處理:aaa111 -> 處理代碼(10行代碼)// 處理:aaa111222 -> 處理代碼(10行代碼)// new Promise((resolve, reject) => {})/* new Promise((resolve, reject) => {setTimeout(() => {resolve('aaa')}, 1000)}).then(res => {// 1.自己處理10行代碼console.log(res, '第一層的10行處理代碼')// 2.對結果進行第一次處理return new Promise((resolve, reject) => {resolve(res + '111')})}).then(res => {console.log(res, '第二層的10處理代碼');return new Promise((resolve, reject) => {resolve(res + '222')})}).then(res => {console.log(res, '第三層的10處理代碼');})*/// 2.// new Promise((resolve, reject) => {// setTimeout(() => {// resolve('aaa')// }, 1000)// }).then(res => {// // 1.自己處理10行代碼// console.log(res,'第一層的10行處理代碼')//// // 2.對結果進行第一次處理// // return new Promise((resolve, reject) => {// // resolve(res + '111')// // })// return Promise.resolve(res + '111')// }).then(res => {// console.log(res, '第二層的10處理代碼');//// return Promise.resolve(res + '222')//// }).then(res => {// console.log(res, '第三層的10處理代碼');// })// 鏈式調用中間某一層出現reject的情況,會直接執行最后面的catch()// new Promise((resolve, reject) => {// setTimeout(() => {// resolve('aaa')// }, 1000)// }).then(res => {// // 1.自己處理10行代碼// console.log(res, '第一層的10行處理代碼')//// // 2.對結果進行第一次處理// // return new Promise((resolve, reject) => {// // resolve(res + '111')// // })// return Promise.reject('error message')// }).then(res => {// console.log(res, '第二層的10處理代碼');//// return Promise.resolve(res + '222')//// }).then(res => {// console.log(res, '第三層的10處理代碼');// }).catch(err => {// console.log(err);// })// thrownew Promise((resolve, reject) => {setTimeout(() => {resolve('aaa')}, 1000)}).then(res => {// 1.自己處理10行代碼console.log(res, '第一層的10行處理代碼')// 2.對結果進行第一次處理// return new Promise((resolve, reject) => {// resolve(res + '111')// })throw 'error message'}).then(res => {console.log(res, '第二層的10處理代碼');return Promise.resolve(res + '222')}).then(res => {console.log(res, '第三層的10處理代碼');}).catch(err => {console.log(err);})// 3.省略掉Promise.resolve// new Promise((resolve, reject) => {// setTimeout(() => {// resolve('aaa')// }, 1000)// }).then(res => {// // 1.自己處理10行代碼// console.log(res, '第一層的10行處理代碼')//// return res + '111'// }).then(res => {// console.log(res, '第二層的10處理代碼');//// return res + '222'//// }).then(res => {// console.log(res, '第三層的10處理代碼');// })</script> </body> </html>4.1 鏈式調用
-
我們在看Promise的流程圖時,發現無論是then還是catch都可以返回一個Promise對象。
-
所以,我們的代碼其實是可以進行鏈式調用的:
-
這里我們直接通過Promise包裝了一下新的數據,將Promise對象返回了
4.2 鏈式調用簡寫
簡化版代碼:
如果我們希望數據直接包裝成Promise.resolve,那么在then中可以直接返回數據
- 注意下面的代碼中,我將return Promise.resovle(data)改成了return data,結果依然是一樣的
4.3 all方法的使用
當需要多個網絡請求都成功后才能進行下一步操作時,可以使用Promise的all方法:
總結
以上是生活随笔為你收集整理的十二、Promise的学习笔记(Promise的基本使用、链式编程、all())的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二十一、PHP框架Laravel学习笔记
- 下一篇: Express接口案例——完成文章增删改