讓你徹底掌握es6 Promise的八段代碼
前言
新的ES6中引入了promise的概念,目的是讓回調(diào)更為優(yōu)雅。層層嵌套的回調(diào)會讓javascript失去美感和可讀性,同時javascript也推薦采用鏈式的方式去書寫函數(shù)調(diào)用。于是Promise就應(yīng)運而生。本文將通過八段代碼讓大家徹底的掌握Promise,下面話不多說,來一起看看詳細的介紹:
1.Promise的立即執(zhí)行性
var p = new Promise(function(resolve, reject){ console.log("create a promise"); resolve("success"); }); console.log("after new Promise"); p.then(function(value){ console.log(value); });
控制臺輸出:
"create a promise" "after new Promise" "success"
Promise對象表示未來某個將要發(fā)生的事件,但在創(chuàng)建(new)Promise時,作為Promise參數(shù)傳入的函數(shù)是會被立即執(zhí)行的,只是其中執(zhí)行的代碼可以是異步代碼。有些同學(xué)會認為,當(dāng)Promise對象調(diào)用then方法時,Promise接收的函數(shù)才會執(zhí)行,這是錯誤的。因此,代碼中"create a promise
"先于"after new Promise
"輸出。
2.Promise 三種狀態(tài)
var p1 = new Promise(function(resolve,reject){ resolve(1); }); var p2 = new Promise(function(resolve,reject){ setTimeout(function(){ resolve(2); }, 500); }); var p3 = new Promise(function(resolve,reject){ setTimeout(function(){ reject(3); }, 500); }); console.log(p1); console.log(p2); console.log(p3); setTimeout(function(){ console.log(p2); }, 1000); setTimeout(function(){ console.log(p3); }, 1000); p1.then(function(value){ console.log(value); }); p2.then(function(value){ console.log(value); }); p3.catch(function(err){ console.log(err); });
控制臺輸出:
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1} Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} 1 2 3 Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2} Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}
Promise的內(nèi)部實現(xiàn)是一個狀態(tài)機。Promise有三種狀態(tài):pending,resolved,rejected。當(dāng)Promise剛創(chuàng)建完成時,處于pending狀態(tài);當(dāng)Promise中的函數(shù)參數(shù)執(zhí)行了resolve后,Promise由pending狀態(tài)變成resolved狀態(tài);如果在Promise的函數(shù)參數(shù)中執(zhí)行的不是resolve方法,而是reject方法,那么Promise會由pending狀態(tài)變成rejected狀態(tài)。
p2、p3剛創(chuàng)建完成時,控制臺輸出的這兩臺Promise都處于pending狀態(tài),但為什么p1是resolved狀態(tài)呢? 這是因為p1 的函數(shù)參數(shù)中執(zhí)行的是一段同步代碼,Promise剛創(chuàng)建完成,resolve方法就已經(jīng)被調(diào)用了,因而緊跟著的輸出顯示p1是resolved狀態(tài)。我們通過兩個setTimeout函數(shù),延遲1s后再次輸出p2、p3的狀態(tài),此時p2、p3已經(jīng)執(zhí)行完成,狀態(tài)分別變成resolved和rejected。
3.Promise 狀態(tài)的不可逆性
var p1 = new Promise(function(resolve, reject){ resolve("success1"); resolve("success2"); }); var p2 = new Promise(function(resolve, reject){ resolve("success"); reject("reject"); }); p1.then(function(value){ console.log(value); }); p2.then(function(value){ console.log(value); });
控制臺輸出:
"success1" "success"
Promise狀態(tài)的一旦變成resolved或rejected時,Promise的狀態(tài)和值就固定下來了,不論你后續(xù)再怎么調(diào)用resolve或reject方法,都不能改變它的狀態(tài)和值。因此,p1中resolve("success2")
并不能將p1的值更改為success2,p2中reject("reject")
也不能將p2的狀態(tài)由resolved改變?yōu)閞ejected.
4.鏈式調(diào)用
var p = new Promise(function(resolve, reject){ resolve(1); }); p.then(function(value){ //第一個then console.log(value); return value*2; }).then(function(value){ //第二個then console.log(value); }).then(function(value){ //第三個then console.log(value); return Promise.resolve('resolve'); }).then(function(value){ //第四個then console.log(value); return Promise.reject('reject'); }).then(function(value){ //第五個then console.log('resolve: '+ value); }, function(err){ console.log('reject: ' + err); })
控制臺輸出:
1 2 undefined "resolve" "reject: reject"
Promise對象的then方法返回一個新的Promise對象,因此可以通過鏈式調(diào)用then方法。then方法接收兩個函數(shù)作為參數(shù),第一個參數(shù)是Promise執(zhí)行成功時的回調(diào),第二個參數(shù)是Promise執(zhí)行失敗時的回調(diào)。兩個函數(shù)只會有一個被調(diào)用,函數(shù)的返回值將被用作創(chuàng)建then返回的Promise對象。這兩個參數(shù)的返回值可以是以下三種情況中的一種:
- return 一個同步的值 ,或者 undefined(當(dāng)沒有返回一個有效值時,默認返回undefined),then方法將返回一個resolved狀態(tài)的Promise對象,Promise對象的值就是這個返回值。
- return 另一個 Promise,then方法將根據(jù)這個Promise的狀態(tài)和值創(chuàng)建一個新的Promise對象返回。
- throw 一個同步異常,then方法將返回一個rejected狀態(tài)的Promise, 值是該異常。
根據(jù)以上分析,代碼中第一個then會返回一個值為2(1*2),狀態(tài)為resolved的Promise對象,于是第二個then輸出的值是2。第二個then中沒有返回值,因此將返回默認的undefined,于是在第三個then中輸出undefined。第三個then和第四個then中分別返回一個狀態(tài)是resolved的Promise和一個狀態(tài)是rejected的Promise,依次由第四個then中成功的回調(diào)函數(shù)和第五個then中失敗的回調(diào)函數(shù)處理。
5.Promise then() 回調(diào)異步性
var p = new Promise(function(resolve, reject){ resolve("success"); }); p.then(function(value){ console.log(value); }); console.log("which one is called first ?");
控制臺輸出:
"which one is called first ?" "success"
Promise接收的函數(shù)參數(shù)是同步執(zhí)行的,但then方法中的回調(diào)函數(shù)執(zhí)行則是異步的,因此,"success"會在后面輸出。
6.Promise 中的異常
var p1 = new Promise( function(resolve,reject){ foo.bar(); resolve( 1 ); }); p1.then( function(value){ console.log('p1 then value: ' + value); }, function(err){ console.log('p1 then err: ' + err); } ).then( function(value){ console.log('p1 then then value: '+value); }, function(err){ console.log('p1 then then err: ' + err); } ); var p2 = new Promise(function(resolve,reject){ resolve( 2 ); }); p2.then( function(value){ console.log('p2 then value: ' + value); foo.bar(); }, function(err){ console.log('p2 then err: ' + err); } ).then( function(value){ console.log('p2 then then value: ' + value); }, function(err){ console.log('p2 then then err: ' + err); return 1; } ).then( function(value){ console.log('p2 then then then value: ' + value); }, function(err){ console.log('p2 then then then err: ' + err); } );
控制臺輸出:
p1 then err: ReferenceError: foo is not defined p2 then value: 2 p1 then then value: undefined p2 then then err: ReferenceError: foo is not defined p2 then then then value: 1
Promise中的異常由then參數(shù)中第二個回調(diào)函數(shù)(Promise執(zhí)行失敗的回調(diào))處理,異常信息將作為Promise的值。異常一旦得到處理,then返回的后續(xù)Promise對象將恢復(fù)正常,并會被Promise執(zhí)行成功的回調(diào)函數(shù)處理。另外,需要注意p1、p2 多級then的回調(diào)函數(shù)是交替執(zhí)行的 ,這正是由Promise then回調(diào)的異步性決定的。
7.Promise.resolve()
var p1 = Promise.resolve( 1 ); var p2 = Promise.resolve( p1 ); var p3 = new Promise(function(resolve, reject){ resolve(1); }); var p4 = new Promise(function(resolve, reject){ resolve(p1); }); console.log(p1 === p2); console.log(p1 === p3); console.log(p1 === p4); console.log(p3 === p4); p4.then(function(value){ console.log('p4=' + value); }); p2.then(function(value){ console.log('p2=' + value); }) p1.then(function(value){ console.log('p1=' + value); })
控制臺輸出:
true false false false p2=1 p1=1 p4=1
Promise.resolve(...)
可以接收一個值或者是一個Promise對象作為參數(shù)。當(dāng)參數(shù)是普通值時,它返回一個resolved狀態(tài)的Promise對象,對象的值就是這個參數(shù);當(dāng)參數(shù)是一個Promise對象時,它直接返回這個Promise參數(shù)。因此,p1 === p2。但通過new的方式創(chuàng)建的Promise對象都是一個新的對象,因此后面的三個比較結(jié)果都是false。另外,為什么p4的then最先調(diào)用,但在控制臺上是最后輸出結(jié)果的呢?因為p4的resolve中接收的參數(shù)是一個Promise對象p1,resolve會對p1”拆箱“,獲取p1的狀態(tài)和值,但這個過程是異步的,可參考下一節(jié)。
8.resolve vs reject
var p1 = new Promise(function(resolve, reject){ resolve(Promise.resolve('resolve')); }); var p2 = new Promise(function(resolve, reject){ resolve(Promise.reject('reject')); }); var p3 = new Promise(function(resolve, reject){ reject(Promise.resolve('resolve')); }); p1.then( function fulfilled(value){ console.log('fulfilled: ' + value); }, function rejected(err){ console.log('rejected: ' + err); } ); p2.then( function fulfilled(value){ console.log('fulfilled: ' + value); }, function rejected(err){ console.log('rejected: ' + err); } ); p3.then( function fulfilled(value){ console.log('fulfilled: ' + value); }, function rejected(err){ console.log('rejected: ' + err); } );
控制臺輸出:
p3 rejected: [object Promise] p1 fulfilled: resolve p2 rejected: reject
Promise回調(diào)函數(shù)中的第一個參數(shù)resolve,會對Promise執(zhí)行"拆箱"動作。即當(dāng)resolve的參數(shù)是一個Promise對象時,resolve會"拆箱"獲取這個Promise對象的狀態(tài)和值,但這個過程是異步的。p1"拆箱"后,獲取到Promise對象的狀態(tài)是resolved,因此fulfilled回調(diào)被執(zhí)行;p2"拆箱"后,獲取到Promise對象的狀態(tài)是rejected,因此rejected回調(diào)被執(zhí)行。但Promise回調(diào)函數(shù)中的第二個參數(shù)reject不具備”拆箱“的能力,reject的參數(shù)會直接傳遞給then方法中的rejected回調(diào)。因此,即使p3 reject接收了一個resolved狀態(tài)的Promise,then方法中被調(diào)用的依然是rejected,并且參數(shù)就是reject接收到的Promise對象。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
js實現(xiàn)千分符和保留幾位小數(shù)的簡單實例
下面小編就為大家?guī)硪黄猨s實現(xiàn)千分符和保留幾位小數(shù)的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之棧詳解
棧作為一種數(shù)據(jù)結(jié)構(gòu),是一種只能在一端進行插入和刪除操作的特殊線性表,也成稱為先進后出表,下面這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)據(jù)結(jié)構(gòu)與算法之棧的相關(guān)資料,需要的朋友可以參考下2022-06-06selenium 反爬蟲之跳過淘寶滑塊驗證功能的實現(xiàn)代碼
這篇文章主要介紹了selenium 反爬蟲之跳過淘寶滑塊驗證功能,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08Js+Dhtml:WEB程序員簡易開發(fā)工具包(預(yù)先體驗版)
Js+Dhtml:WEB程序員簡易開發(fā)工具包(預(yù)先體驗版)...2006-11-11對javascript的一點點認識總結(jié)《javascript高級程序設(shè)計》讀書筆記
Javascript專為與網(wǎng)頁交互而設(shè)計的腳本語言,由下列三個部門構(gòu)造2011-11-11javascript中日期函數(shù)new Date()的瀏覽器兼容性問題
這篇文章主要介紹了javascript中日期函數(shù)new Date()的瀏覽器兼容性問題,需要的朋友可以參考下2015-09-0950行代碼實現(xiàn)Webpack組件使用次數(shù)統(tǒng)計
這篇文章主要介紹了50行代碼實現(xiàn)Webpack組件使用次數(shù)統(tǒng)計,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03