關於promise我在以前的文章已經應用過好幾回,如[js高手之路]Node.js+jade+express+mongodb+mongoose+promise實現todolist,本文就來說解下promise的常見用法.javascript
爲何會有promise,他的做用是什麼?html
promise主要是爲了解決js中多個異步回調難以維護和控制的問題.java
什麼是promise?node
從圖中,咱們能夠看出,Promise是一個函數,這個函數上有在項目中經常使用的靜態方法:all, race, reject,resolve等,原型對象上有有catch, then等方法.也就是說,若是要調用catch和then方法,須要一個Promise的實例( new Promise ), 而靜態方法能夠用函數自己調用,如:Promise.all, Promise.race等,好了,至此,至少知道Promise是什麼,可是仍是不知道怎麼用他提供的方法,接下來咱們從一個需求開始,在javascript中,常常有這樣的需求:mongodb
1,頁面上有一個按鈕,一個ul,點擊按鈕的時候,每隔1秒鐘向ul的後面追加一個li, 一共追加10個,li的內容從0開始技術( 0, 1, 2, ....9 ),這個在個人博客文章中,出現好幾回express
2,每隔1秒鐘輸出遞增的數字,如( 1, 2, 3, 4, 5 等 )promise
3,紅綠燈( 紅->(等2秒)->綠( 等2秒 )->黃( 等2秒 ) -> 紅等,如此循環下去dom
4,node.js中的爬蟲,爬文章url->爬文章內容->爬文章url->爬文章內容異步
好了,太多這類應用了,這類應用叫作異步回調( 執行完一件事,才能接着往下執行),若是出現屢次,就會產生多層嵌套( 回調地獄 ),維護和控制異步過程很是的麻煩和困難,如:mongoose
每隔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還有其餘的用法,在項目中,這些是比較經常使用到的