ES6中Promise的使用方法例項總結
本文例項講述了ES6中Promise的使用方法。分享給大家供大家參考,具體如下:
在javascript中,程式碼是單執行緒執行的,對於一些比較耗時的IO操作,都是通過非同步回撥函式來實現的。
但是這樣會存在一個問題,當下一個的操作需要上一個操作的結果時,我們只能把程式碼嵌到上一個操作的回撥函式裡,這樣一層嵌一層,最終形成回撥地獄。
$.get('/login.php',function (login) { $.get('/user.php',function (user) { $.get('/info.php',function (info) { //程式碼就這樣一層嵌一層,不夠直觀,維護也麻煩 }); }); });
為了解決這種問題,ES6中就提供了Promise方法來解決這種問題。
Promise是一個建構函式,通過它,我們可以建立一個Promise例項物件。
let p = new Promise(function (resolve,reject) { setTimeout(() => { console.log('OK'); resolve('OK'); },1000); });
Promise建構函式接受一個函式作為引數,這個函式有兩個引數,resolve和reject。
resolve函式是將Promise的狀態設定為fulfilled(完成),reject函式是將Promise的狀態設定為rejected(失敗)。
上述程式碼,我們並沒有進行任何呼叫,當執行時,間隔1秒後輸出了'OK'。所以這裡需要注意,我們通常使用Promise時,需要在外層再包裹一層函式。
let p = function () { return new Promise(function (resolve,reject) { setTimeout(() => { console.log('OK'); resolve('OK'); },1000); }); }; p();
上面的程式碼p();返回的是一個Promise例項物件,Promise物件上有 then(),catch(),finally() 方法。
then方法有兩個引數,onFulfilled和onRejected,都是函式。
onFulfilled用於接收resolve方法傳遞過來的資料,onRejected用於接收reject方法傳遞過來的資料。
let p = function () { return new Promise(function (resolve,reject) { setTimeout(() => { if (Math.random() > 0.5) { resolve('OK'); } else { reject('ERR'); } },1000); }); }; p().then(function (data) { console.log('fulfilled',data); },function (err) { console.log('rejected',err); });
then()方法總是會返回一個Promise例項,這樣我們就可以一直呼叫then()。
在then方法中,你既可以return 一個具體的值 ,還可以return 一個Promise物件。
如果直接return的是一個數據,那then方法會返回一個新Promise物件,並以該資料進行resolve。
let p = function () { return new Promise(function (resolve,reject) { resolve(1); }); }; p().then(function (data) { console.log(`第 ${data} 次呼叫`); //注意這裡直接返回的值 //then會建立一個新的Promise物件,並且以返回的值進行resolve //那麼該值會被下面的then方法的onFulfilled回撥拿到 return ++data; }).then(function (data) { console.log(`第 ${data} 次呼叫`); return ++data; }).then(function (data) { console.log(`第 ${data} 次呼叫`); return ++data; });
如果返回的是一個Promise物件,請看下面程式碼。
let p = function () { return new Promise(function (resolve,reject) { resolve(1); }); }; p().then(function (data) { console.log(`第 ${data} 次呼叫`); return new Promise(function (resolve,reject) { resolve(++data); }); }).then(function (data) { console.log(`第 ${data} 次呼叫`); return new Promise(function (resolve,reject) { resolve(++data); }); });
其實效果與直接返回值的是一樣的。
即然then()可以進行鏈式操作,那我們最早之前的回撥地獄寫法,就可以通過它進行改進了。
function login() { return new Promise(function (resolve,reject) { $.get('/login.php',function (result) { resolve(result); }); }); } function user(data) { return new Promise(function (resolve,reject) { $.get('/user.php',function (result) { resolve(result); }); }); } function info(data) { return new Promise(function (resolve,reject) { $.get('/info.php',function (result) { resolve(result); }); }); } login().then(function (data) { console.log('處理login'); //把login非同步處理獲取的資料,傳入到下一個處理中。 return user(data); }).then(function (data) { console.log('處理user'); //把user非同步處理獲取的資料,傳入到下一個處理中。 return info(data); }).then(function (data) { console.log('處理info'); });
這樣修改後,回撥地獄層層巢狀的結構就變的清晰多了。上述程式碼是虛擬碼。
Promise物件還有一個catch方法,用於捕獲錯誤,該方法與 then(null,onRejected) 等同,是一個語法糖。
let p = function () { return new Promise(function (resolve,reject) { resolve('開始'); }); }; p().then(function (data) { console.log('1'); return new Promise(function (resolve,reject) { reject('錯誤1'); }); }).then(function (data) { console.log('2'); return new Promise(function (resolve,reject) { reject('錯誤2'); }); }).then(function (data) { console.log('3'); return new Promise(function (resolve,reject) { reject('錯誤3'); }); }).catch(function (reason) { console.log(reason); });
注意,一旦操作中有錯誤發生,則會進入到catch中,後面的操作將不再執行。
Promise物件內部自帶了try catch,當代碼執行時錯誤,會自動以錯誤物件為值reject,並最終被catch捕獲。
let p = function () { return new Promise(function (resolve,reject) { resolve('開始'); }); }; p().then(function (data) { //注意這裡列印了一個未定義的變數 console.log(a); }).catch(function (reason) { //這裡會捕獲到錯誤 console.log('rejected'); console.log(reason); });
Promise還提供了,all(),race(),reject(),resolve()等在建構函式上的方法,呼叫這些方法並不需要例項化物件。
all()方法,可以讓我們並行的執行非同步操作,直到所有操作完成了,才執行回撥。
function fn1() { return new Promise(function (resolve,reject) { setTimeout(function () { resolve('fn1'); },1000); }); } function fn2() { return new Promise(function (resolve,reject) { setTimeout(function () { resolve('fn2'); },2000); }); } function fn3() { return new Promise(function (resolve,reject) { setTimeout(function () { resolve('fn3'); },3000); }); } //all會等待所有操作完成,會把所有操作的結果放到一個數組中,傳給then。 Promise.all([fn1(),fn2(),fn3()]).then(function (data) { console.log(data); });
race()方法是誰先處理完,就以誰為準,把最先處理完的結果傳給then。
function fn1() { return new Promise(function (resolve,3000); }); } //race是以誰先處理完,就以誰為準,fn1最先處理完,那fn1的結果會傳給then //注意這裡fn2和fn3還是會執行,不過不會進入then了 Promise.race([fn1(),fn3()]).then(function (data) { console.log(data); });
reject()方法,返回一個帶有拒絕原因reason引數的Promise物件。
// Promise.reject('錯誤') // 等同於 // new Promise(function(resolve,reject) { // reject('錯誤'); // }); let p = Promise.reject('錯誤'); p.then(function (data) { }).catch(function (reason) { console.log(reason); });
resolve()方法,根據傳入的值返回一個Promise物件。
//如果傳入的引數是普通值,則返回一個新Promise物件,並以該值resolve let p1 = Promise.resolve('OK'); p1.then(function (data) { console.log(data); }); //如果傳入的引數是一個Promise物件,則原封不動的返回該Promise物件 let obj = new Promise(function (resolve,reject) { resolve('我是Promise物件'); }); let p2 = Promise.resolve(obj); p2.then(function (data) { console.log(data); console.log(p2 === obj); }); //如果傳入的引數是一個thenable物件(帶有then方法), //會轉換成Promise物件,並執行thenable物件的then方法 let then = { then(resolve,reject) { resolve('我是thenable物件'); } } let p3 = Promise.resolve(then); p3.then(function (data) { console.log(data); }); //如果什麼引數都不傳入,則返回狀態為resolved的Promise物件 let p4 = Promise.resolve(); p4.then(function (data) { console.log(data); }).catch(function (reason) { console.log(reason); });
感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。
更多關於JavaScript相關內容可檢視本站專題:《JavaScript操作DOM技巧總結》、《JavaScript頁面元素操作技巧總結》、《JavaScript事件相關操作與技巧大全》、《JavaScript查詢演算法技巧總結》、《JavaScript資料結構與演算法技巧總結》、《JavaScript遍歷演算法與技巧總結》及《JavaScript錯誤與除錯技巧總結》
希望本文所述對大家JavaScript程式設計有所幫助。