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