1.通俗點說,promise就是一種異步操做的解決方案,提及異步操做,那有必要先講講「同步」和「異步」的概念。javascript
舉個例子,好比中午作飯,先淘米,等飯熟了,而後炒菜,等菜熟了再把衣服丟經洗衣機裏去洗衣服,就是說你在等一件事完成後,你纔開始作下一步的事情,這就是同步,而在代碼中的同步就是從上而下,逐行執行,以下所示:java
function aa(){ console.log('第一步,先淘米,如今飯熟了') } function bb(){ console.log('第二步,個人菜熟了') } function cc(){ console.log('第三步,洗衣機洗衣服') } aa() bb() cc()
在你淘好米的同時,你就立刻把衣服丟經洗衣機裏,接着就開始炒菜,幾件事同時進行,有可能飯先熟,也有可能菜先熟,也有可能衣服先洗好ajax
function aa(fn){ console.log('第一步,先淘米,如今飯熟了') fn() } function bb(){ console.log('第二步,個人菜熟了') } function cc(){ console.log('第三步,洗衣機洗衣服') } aa(cc) bb()
解釋:你們有沒有發現,我是以回調函數的方式將函數cc,傳入函數aa的?沒錯,回調函數也是最基礎最經常使用的處理js異步操做的辦法。promise
Promise是一個構造函數,本身身上有all、reject、resolve這幾個眼熟的方法,原型上有then、catch等一樣很眼熟的方法。異步
先new一個再說:函數
var data = new Promise(function(resolve, reject){ setTimeout(function(){ console.log('執行完成'); resolve('數據1'); }, 2000); });
Promise的構造函數接收一個參數,是函數,而且傳入兩個參數:resolve,reject,分別表示異步操做執行成功後的回調函數和異步操做執行失敗後的回調函數。spa
在上面的代碼中,咱們執行了一個異步操做,也就是setTimeout,2秒後,輸出「執行完成」,而且調用resolve方法。3d
運行代碼,會在2秒後輸出「執行完成」。注意!我只是new了一個對象,並無調用它,咱們傳進去的函數就已經執行了,這是須要注意的一個細節。因此咱們用Promise的時候通常是包在一個函數中,在須要的時候去運行這個函數,如:code
function fn1(){ var data = new Promise(function(resolve, reject){ setTimeout(function(){ console.log('執行完成'); resolve('數據1'); }, 2000); }); return data; }
看到這裏,估計你確定有點蒙B,寫的究竟是啥破玩意啊,你到底想幹什麼啊,別急,聽我慢慢道來,還記得Promise對象上有then、catch方法吧?這就是強大之處了對象
fn1().then(function(data){ console.log(data); });
意思就是說,當函數fn1執行完成後,調用promise上的then方法,在then方法裏面接受一個data參數,這個data參數就是fn1裏面傳過來的,你能夠在then裏面對傳過來的data參數進行操做,也能夠繼續執行其餘函數,看到這裏你可能已經明白了,但你必定會有一個疑問,就算說出花來,你這樣不就是在玩回調嘛!我下面那樣寫結果還不同:
function fn1(callback){ callback('數據1') } function callback(data){ console.log(data) } fn1(callback)
但是你想過沒有,你如今還只是在函數裏嵌套一個函數,若是讓你嵌套10個呢?100個呢?你就會陷入常說的回調地獄,你肯定你不會瘋掉?
因此,從表面上看,Promise只是可以簡化層層回調的寫法,而實質上,Promise的精髓是「狀態」,用維護狀態、傳遞狀態的方式來使得回調函數可以及時調用,它比傳遞callback函數要簡單、靈活的多。因此使用Promise的正確場景是這樣的
function fn1(){ var data = new Promise(function(resolve, reject){ resolve('數據1'); }); return data; } function fn2(){ var data = new Promise(function(resolve, reject){ resolve('數據2'); }); return data; } function fn3(){ var data = new Promise(function(resolve, reject){ resolve('數據3'); }); return data; } fn1() .then(function(data){ console.log(data); return fn2(); }) .then(function(data){ console.log(data); return fn3(); }) .then(function(data){ console.log(data); });
它是作什麼用的呢?其實它和then的第二個參數同樣,用來指定reject的回調,用法是這樣:
function fn1(){ var data = new Promise(function(resolve, reject){ var num = 3; if(num>3){ resolve('num大於3'); }else{ reject('num小於3') } }); return data; } fn1() .then(function(data){ console.log(data); }) .catch(function(reason){ console.log('rejected'); console.log(reason); });
看看打印結果:
這個方法用的很少,不少人都會忽略這個方法,仍是講講吧,意思是無論Promise 對象最後狀態如何,都會執行的操做
function fn1(){ var data = new Promise(function(resolve, reject){ var num = 3; if(num>3){ resolve('num大於3'); }else{ reject('num小於3') } }); return data; } fn1() .then(function(data){ console.log(data); }) .catch(function(reason){ console.log('rejected'); console.log(reason); }) .finally(()=>{ console.log('執行了finally') })
這個all方法我的以爲真的比較實用,前段時間作項目的時候,產品經理有那麼一個需求,當用戶每次在input框裏輸入內容的時候,都要實時的將後臺返回的查詢結果實時渲染在列表中,以供用戶查看,需求看起來很簡單,不就是監聽一下input框的value值嗎?value每改變一次,就發送一次ajax的請求,將後臺最後返回的數據渲染出來就行了呀!但是事實真的那麼簡單嗎?我舉個例子:好比用戶想查【中國建設銀行】’,
按道理來講,我只要將最後一次返回的【中國建設銀行】渲染在li中就大功告成了吧?但事實真的是這樣嗎?
你們知道,發送一次請求是有響應時間的,若是第一次響應時間花費了4s ,而第二次只響應時間花費了2s呢??那不是就出現了,用戶精確搜索【中國建設銀行】,你卻給用戶看【中國建設銀行,中國農業銀行,中國工商銀行】,這樣就尷尬了吧?因此這也是異步操做帶來的坑,
那有沒有辦法讓這裏的異步變成相似同步請求呢?接下來promise的all方法就閃亮登場了,
function fn1(){ var data = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('數據1') },1000) }); return data; } function fn2(){ var data = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('數據2') },3000) }); return data; } function fn3(){ var data = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('數據3') },2000) }); return data; } Promise.all([fn1(),fn2(),fn3()]).then((data)=>{ console.log(data) })
上面代碼我fn1,fn2,fn3, 的數據分別延時1s, 3s , 2s 輸出,來模擬異步請求,你們想一想最後console.log(data),這個data會輸出什麼?
會是['數據1',‘數據3’,‘數據2’]嗎?
下面就是見證奇蹟的時刻了:
看到沒?是否是很神奇??