原来你是这样的Promise
1. Promise簡介
promise是異步編程的一種解決方案,它出現(xiàn)的初衷是為了解決回調(diào)地獄的問題。
打個比方,我需要:
--(延遲1s)--> 輸出1 --(延遲2s)--> 輸出2 --(延遲3s)--> 輸出3通常寫法:
setTimeout(()=> {console.log('1');setTimeout(()=> {console.log('2');setTimeout(()=> {console.log('3'); }, 3000)}, 2000) }, 1000)這樣的多重的嵌套的回調(diào)被稱為回調(diào)地獄,這樣的代碼可讀性很差,不利于理解。
如果用promise的話畫風(fēng)一轉(zhuǎn)
function delay(time, num) {return new Promise((res, rej)=> {setTimeout(()=> {console.log(num);res();}, time*1000)}); } delay(1, 1).then(()=> {return delay(2, 2); }).then(()=> {delay(3, 3); }) 使用了promise的鏈?zhǔn)秸{(diào)用,代碼結(jié)構(gòu)更清晰。是不是很棒?那還不趕快get起來~
2. Promise的使用
調(diào)用方式如下:
new Promise((resolve, reject)=> {if('some option') {resolve('some value');} else {reject('some error');} }).then(val=> {// ... },error=> {// ... } )Promise構(gòu)造函數(shù)接收一個函數(shù)型參數(shù)fn,fn有兩個參數(shù),分別是:resolve、reject,Promise還有一個Promise.prototype.then方法,該方法接收兩個參數(shù),分別是成功的回調(diào)函數(shù)succ和失敗的回調(diào)函數(shù)error。
在fn中調(diào)用resolve會觸發(fā)then中的succ回調(diào),調(diào)用reject會觸發(fā)error回調(diào)。
2.1 參數(shù)傳遞
- 在fn內(nèi)部調(diào)用resolve/reject傳入的參數(shù)會作為相應(yīng)參數(shù)傳入相應(yīng)的回調(diào)函數(shù) new Promise((res, rej)=> {res('happy') }).then(val=> {console.log(val); // happy });new Promise((res, rej)=> {rej('error!'); }).then(val=> {}, err=> {console.log(err); // error! });
- 鏈?zhǔn)秸{(diào)用時若上一級沒有傳遞值則默認(rèn)為undefined new Promise((res, rej)=> {res('a'); }).then(val=> {return 'b' }).then(val=> {console.log(val); // 'b' }).then((val)=> {console.log(val); // 'undefined' });
- 若上一級的then中傳遞的并非函數(shù),則忽略該級 new Promise((res, rej)=> {res('a'); }).then(val=> {return 'b'; }).then(val=> {console.log(val); // 'b'return 'c'; }).then({ // 并非函數(shù)name: 'lan' }).then((val)=> {console.log(val); // 'c' });
2.2 參數(shù)傳遞例題
let doSomething = function() {return new Promise((resolve, reject) => {resolve('返回值');}); };let doSomethingElse = function() {return '新的值'; }doSomething().then(function () {return doSomethingElse(); }).then(resp => {console.warn(resp);console.warn('1 =========<'); });doSomething().then(function () {doSomethingElse(); }).then(resp => {console.warn(resp);console.warn('2 =========<'); });doSomething().then(doSomethingElse()).then(resp => {console.warn(resp);console.warn('3 =========<'); });doSomething().then(doSomethingElse).then(resp => {console.warn(resp);console.warn('4 =========<'); });結(jié)合上面的講解想一想會輸出什么?(答案及解析)
3. Promise.prototype.then
當(dāng)Promise中的狀態(tài)(pending?--->?resolved?or?rejected)發(fā)生變化時才會執(zhí)行then方法。
- 調(diào)用then返回的依舊是一個Promise實例 ( 所以才可以鏈?zhǔn)秸{(diào)用... )
- then中的回調(diào)總會異步執(zhí)行
- 如果你不在Promise的參數(shù)函數(shù)中調(diào)用resolve或者reject那么then方法永遠(yuǎn)不會被觸發(fā)
4. Promise的靜態(tài)方法
Promise還有四個靜態(tài)方法,分別是resolve、reject、all、race,下面我們一一介紹一下。
4.1 Promise.resolve()
除了通過new Promise()的方式,我們還有兩種創(chuàng)建Promise對象的方法,Promise.resolve()相當(dāng)于創(chuàng)建了一個立即resolve的對象。如下兩段代碼作用相同:
Promise.resolve('a');new Promise((res, rej)=> {res('a'); });當(dāng)然根據(jù)傳入的參數(shù)不同,Promise.resolve()也會做出不同的操作。
- 參數(shù)是一個 Promise 實例
如果參數(shù)是 Promise 實例,那么Promise.resolve將不做任何修改、原封不動地返回這個實例。
- 參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象,比如下面這個對象。
let thenable = {then: function(resolve, reject) {resolve(42);} };Promise.resolve方法會將這個對象轉(zhuǎn)為 Promise對象,然后就立即執(zhí)行thenable對象的then方法。
- 參數(shù)不是具有then方法的對象,或根本就不是對象
如果參數(shù)是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的 Promise 對象,狀態(tài)為resolved。
- 不帶有任何參數(shù)
Promise.resolve方法允許調(diào)用時不帶參數(shù),直接返回一個resolved狀態(tài)的 Promise 對象。
值得注意的一點是該靜態(tài)方法是在本次事件輪詢結(jié)束前調(diào)用,而不是在下一次事件輪詢開始時調(diào)用。關(guān)于事件輪詢可以看這里——>JavaScript 運行機(jī)制詳解:再談Event Loop
4.2 Promise.reject()
和Promise.resolve()類似,只不過一個是觸發(fā)成功的回調(diào),一個是觸發(fā)失敗的回調(diào)
4.3 Promise.all()
Promise的all方法提供了并行執(zhí)行異步操作的能力,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。
function asyncFun1() {return new Promise((res, rej)=> {setTimeout(()=> { res('a');}, 1000);}); } function asyncFun2() {return new Promise((res, rej)=> {setTimeout(()=> { res('b');}, 1000);}); } function asyncFun3() {return new Promise((res, rej)=> {setTimeout(()=> { res('c');}, 1000);}); } Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {console.log(val); }); Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {console.log(val); // ['a', 'b', 'c'] });用Promise.all來執(zhí)行,all接收一個數(shù)組參數(shù),里面的值最終都算返回Promise對象。這樣,三個異步操作的并行執(zhí)行的,等到它們都執(zhí)行完后才會進(jìn)到then里面。有了all,你就可以并行執(zhí)行多個異步操作,并且在一個回調(diào)中處理所有的返回數(shù)據(jù)。
適用場景:打開網(wǎng)頁時,預(yù)先加載需要用到的各種資源如圖片、flash以及各種靜態(tài)文件。所有的都加載完后,我們再進(jìn)行頁面的初始化。
4.4 Promise.race()
race()和all相反,all()是數(shù)組中所有Promise都執(zhí)行完畢就執(zhí)行then,而race()是一旦有一個Promise執(zhí)行完畢就會執(zhí)行then(),用上面的三個Promise返回值函數(shù)舉例
Promise.race([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {console.log(val); // a });5. 鏈?zhǔn)秸{(diào)用經(jīng)典例題
看了這么多關(guān)于Promise的知識,我們來做一道題鞏固一下。
寫一個類Man實現(xiàn)以下鏈?zhǔn)秸{(diào)用調(diào)用方式: new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');打印: 'hello, lan' -(等待3s)--> 'lan eat apple' -(等待5s)--> 'lan eat banana'
思路:
- 在原型方法中返回this達(dá)到鏈?zhǔn)秸{(diào)用的目的
- 等待3s執(zhí)行的效果可以用Promise & then實現(xiàn)
具體實現(xiàn)如下:
class Man {constructor(name) {this.name = name;this.sayName();this.rope = Promise.resolve(); // 定義全局Promise作鏈?zhǔn)秸{(diào)用 }sayName() {console.log(`hello, ${this.name}`);}sleep(time) {this.rope = this.rope.then(()=> {return new Promise((res, rej)=> {setTimeout(()=> {res();}, time*1000);});});return this;}eat(food) {this.rope = this.rope.then(()=> {console.log(`${this.name} eat ${food}`); });return this;} }new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');ok!不知道你有沒有看懂呢?如果能完全理解代碼那你的Promise可以通關(guān)了,順便來個小思考,下面這種寫法可以嗎?和上面相比有什么區(qū)別?:
class Man1345 {constructor(name) {this.name = name;this.sayName(); }sayName() {console.log(`hello, ${this.name}`);}sleep(time) { this.rope = new Promise((res, rej)=> {setTimeout(()=> {res();}, time*1000);}); return this;}eat(food) {this.rope = this.rope.then(()=> { console.log(`${this.name} eat ${food}`); });return this;} }new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');簡單的說,第二段代碼的執(zhí)行結(jié)果是
'hello, lan' -(等待3s)--> 'lan eat apple' ---> 'lan eat banana'為什么會出現(xiàn)這種差別? 因為第二段代碼每一次調(diào)用sleep都會new一個新的Promise對象,調(diào)用了兩次sleep就new了兩個Promise對象。這兩個對象是異步并行執(zhí)行,會造成兩句eat同時顯示。
和以下情況類似
var time1 = setTimeout(()=> {console.log('a'); }, 1000) var time2 = setTimeout(()=> {console.log('b'); }, 1000) // 同時輸出 a b抽象一點的講解是:
// 第一段正確的代碼的執(zhí)行為 var p1 = new Promise().then('停頓3s').then('打印食物').then('停頓5s').then('打印食物');// 第二段代碼的執(zhí)行行為,p1、p2異步并行執(zhí)行 var p1 = new Promise().then('停頓3s').then('打印食物'); var p2 = new Promise().then('停頓5s').then('打印食物'); 總結(jié)Promise的經(jīng)常用到的地方:
- 擺脫回調(diào)地獄
- 多個異步任務(wù)同步
Promise是我們的好幫手,不過還有另一種方法也可以做到,那就是async&await,可以多多了解一下。
參考資料
ECMAScript 6 入門
通俗淺顯的理解Promise中的then
大白話講解promise
轉(zhuǎn)載于:https://www.cnblogs.com/wind-lanyan/p/8849643.html
總結(jié)
以上是生活随笔為你收集整理的原来你是这样的Promise的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python进程学习笔记-multipr
- 下一篇: Visual Studio2010安装步