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