關於promise我在之前的文章已經應用過好幾次,如[js高手之路]Node.js+jade+express+mongodb+mongoose+promise實現todolist,本文就來講解下promise的常見用法.
為什么會有promise,他的作用是什么?
promise主要是為了解決js中多個異步回調難以維護和控制的問題.
什么是promise?
從圖中,我們可以看出,Promise是一個函數,這個函數上有在項目中常用的靜態方法:all, race, reject,resolve等,原型對象上有有catch, then等方法.也就是說,如果要調用catch和then方法,需要一個Promise的實例( new Promise ), 而靜態方法可以用函數本身調用,如:Promise.all, Promise.race等,好了,至此,至少知道Promise是什么,但是還是不知道怎么用他提供的方法,接下來我們從一個需求開始,在javascript中,經常有這樣的需求:
1,頁面上有一個按鈕,一個ul,點擊按鈕的時候,每隔1秒鍾向ul的后面追加一個li, 一共追加10個,li的內容從0開始技術( 0, 1, 2, ....9 ),這個在我的博客文章中,出現好幾次
2,每隔1秒鍾輸出遞增的數字,如( 1, 2, 3, 4, 5 等 )
3,紅綠燈( 紅->(等2秒)->綠( 等2秒 )->黃( 等2秒 ) -> 紅等,如此循環下去
4,node.js中的爬蟲,爬文章url->爬文章內容->爬文章url->爬文章內容
好了,太多這類應用了,這類應用叫做異步回調( 執行完一件事,才能接着往下執行),如果出現多次,就會產生多層嵌套( 回調地獄 ),維護和控制異步過程非常的麻煩和困難,如:
每隔1秒鍾輸出遞增的數字,如( 1, 2, 3 等 )
1 setTimeout(function () { 2 console.log(1); 3 setTimeout(function () { 4 console.log(2); 5 setTimeout(function () { 6 console.log(3); 7 }, 1000); 8 }, 1000); 9 }, 1000);
這還是3層,如果4層,5層。。。。,現在代碼簡單,可能感覺不到,如果console.log換成具體的業務邏輯,那就不得了,假如每個真實的業務邏輯有100行代碼,5層嵌套。而業務邏輯中又有很多的嵌套匹配。你能想象嗎?如果由於業務需求需要 調換業務的執行順序?是不是很頭疼?不用擔心,Promise可以幫你非常靈活的調整:
1 function next( n ){ 2 return new Promise( function( resolve, reject ){ 3 setTimeout( function(){ 4 resolve( n ); 5 }, 1000 ); 6 } ); 7 } 8 next( 1 ).then( function( res ){ 9 console.log( res ); 10 return next( 2 ); 11 } ).then( function( res ){ 12 console.log( res ); 13 return next( 3 ); 14 } ).then( function( res ){ 15 console.log( res ); 16 } )
Promise的構造函數接收一個參數,是函數,並且傳入兩個參數:resolve,reject,分別表示異步操作執行成功后的回調函數和異步操作執行失敗后的回調函數。按照標准來講,其實resolve是將Promise的狀態置為fullfiled,reject是將Promise的狀態置為rejected,promise的常用用法.
一般是用一個函數嵌套,返回一個promise對象,為什么這么用?
因為then,catch方法需要一個Promise實例,才能把多個異步執行的操作,根據resolve和reject的執行狀態一層層往下執行.
當我們執行next( 1 )的時候,在next函數中返回一個promise對象,一秒鍾之后,通過resolve把n( 就是 1 )傳遞給then方法的第一個function,參數res就是resolve傳遞過來的數據,所以1秒鍾后輸出1,緊接着我在return next( 2 ),這個時候又調用了一次Promise對象,一秒鍾后,通過resolve把n ( 就是 2 )傳遞給下一個then方法的第一個function, 參數res就收到n( 2 )的值,所以1秒鍾后輸出2。。。下面輸出3的過程跟剛才分析的一樣,有一點一定要注意,在then方法中的function 調用的next方法,一定要用return ,否則不會通過resolve把數據往下傳遞( 通俗點講就是下一個異步操作,接收不到上一步的結果 ).
then中的function也可以return一個值,把一個值往下傳遞
1 function next( n ){ 2 return new Promise( function( resolve, reject ){ 3 setTimeout( function(){ 4 resolve( n ); 5 }, 1000 ); 6 } ); 7 } 8 next( 1 ).then( function( res ){ 9 console.log( res ); 10 return 2; 11 } ).then( function( res ){ 12 console.log( res ); 13 return 3; 14 } ).then( function( res ){ 15 console.log( res ); 16 } )
reject是把數據傳遞給then方法的第二個function處理,then方法可以接收2個參數
1 function next(){ 2 return new Promise( function( resolve, reject ){ 3 var num = Math.floor( Math.random() * 10 ); 4 if ( num <= 5 ) { 5 resolve( num ); 6 }else { 7 reject( new Error() ); 8 } 9 } ); 10 } 11 next( 1 ).then( function( res ){ 12 console.log( res ); 13 }, function( res ){ 14 console.log( res ); 15 } ); 16 console.log( '正常執行' );
當隨機數大於等於6的時候,我把一個錯誤往下拋,然后在then的第二個參數接收到,整個程序還是能夠正常運行.
catch也是接收reject傳遞的數據
1 function next(){ 2 return new Promise( function( resolve, reject ){ 3 var num = Math.floor( Math.random() * 10 ); 4 if ( num <= 5 ) { 5 resolve( num ); 6 }else { 7 reject( new Error() ); 8 } 9 } ); 10 } 11 next( 1 ).then( function( res ){ 12 console.log( res ); 13 } ).catch( function( res ){ 14 console.log( 'reject:' + res ); 15 } ); 16 console.log( '正常執行' );
Promise.all是等所有的異步資源都加載完畢之后,再執行代碼,比如。頁面有很多的插件庫,一般都是要加載完畢之后,才能有特效效果。Promise.all主要解決的是多個異步模塊的依賴問題,必須等大家都加載完畢之后,才執行( 說白了,就是等最慢的那個異步操作執行完了,再打印出結果 )
1 var count = 0; 2 function next() { 3 return new Promise(function (resolve, reject) { 4 var num = Math.floor(Math.random() * 10); 5 setTimeout( function(){ 6 console.log( num ); 7 resolve( `第${++count}隨機到的值是${num}`); 8 }, 2000 ); 9 }); 10 } 11 Promise.all( [ next(), next(), next() ] ).then( function( res ){ 12 console.log( res ); 13 } );
三個next()異步操作執行完畢之后,才會一起把他們的resolve結果打印出來
Promise.race,只要最快的異步執行完畢之后,就執行then,不會等待其他的異步操作
Promise還有其他的用法,在項目中,這些是比較常用到的