Promise是ES6提供的一種解決異步編程的方案。web
Promise的本質是一個構造函數,咱們能夠使用new Promise來建立Promise實例對象,利用Promise對象的方法進行異步編程。ajax
1. Promise定義回調函數的方式更靈活。sql
使用純回調函數解決異步問題,回調函數必須在一開始就被指定,而Promise的回調函數能夠在啓動了異步任務後指定,甚至能夠在異步任務結束後指定。編程
2. Promise能夠解決回調函數由於嵌套形成的回調地獄。promise
若是後面的異步操做,須要拿到前面異步操做的數據後才能執行,使用純回調函數,會出現層層嵌套的問題。形成的結果是代碼不易閱讀,不易維護。bash
Promise的三種狀態——pending、resolved、rejectedkoa
新建一個Promise對象時,初始的狀態爲pending(等待中),調用resolve(),狀態變爲resolved/fullfilled(成功)。調用reject(),狀態變爲rejected(失敗)。ssh
狀態一旦改變,就沒法再改變。也就是說,若是已經調用了resolve()和reject()中任意一個函數,再進行調用,Promise狀態不會再發生改變。異步
簡述:一開始,咱們經過new Promise()建立一個Promise對象,此時的狀態爲pending。promise的參數接收兩個函數,分別是resolve和reject。在成功的時候調用resolve(),在失敗的時候調用reject()。resolve()和reject()中的任意一個被調用後,promise的狀態發生對應的改變(resloved/rejected)。resolve()和reject()的調用是promise狀態的標識,也是後續異步操做的指路燈。只有在狀態確認後,才能進行對應的成功/失敗的異步操做,也就是執行then/catch中的回調函數。執行完回調函數後,會返回一個新的Promise對象。ide
Promise代碼例子
const promise=new Promise((reslove,reject)=>{ //執行器函數(executor)--> 同步回調函數 setTimeout(()=>{ if(Date.now()%2==0){ //成功時調用resolve() reslove('success '+Date.now()); //reslove和reject中均可以傳入值,且只能傳入一個值 }else{ //失敗時調用reject() reject('failed '+Date.now()); } },1000)})
//then和catch會接收在resolve和reject中傳入的數據
//1. then能夠接收resolve和reject的值(resolve在前,reject在後)promise.then(value=>{ console.log(value); //onresolved回調函數 這裏的value接收的就是'success '+Date.now()},reason=>{ console.log(reason); //onrejected回調函數 這裏的reason接收的就是'failed '+Date.now()})
//2. catch只能接收reject的值promise.then(value=>{ console.log(value); }).catch(reason=>{ console.log(reason);})
//3. 也能夠只接收成功/失敗的值promise.then(value=>{ console.log(value); })promise.then(null,reason=>{ console.log(reason); })複製代碼
promise中是先肯定狀態仍是先肯定回調?
咱們能夠經過定時器的方法人爲地改變狀態和回調的執行順序,無論怎樣都是能夠執行的。可是在程序執行的過程當中,永遠都是先確認狀態,再執行回調函數的。
咱們都知道,JavaScript的執行是單線程的,同步任務會率先一件一件得在主線程完成,而異步任務會進入任務隊列,等待同步任務完成後執行。
而任務對列中還分爲了宏任務和微任務。
宏任務:script,DOM事件函數,ajax,定時器等。
微任務:promise等。
Promise做爲典型的微任務,須要注意的是new Promise中的回調函數(也就是執行器函數)是當即執行的,也就是說執行器函數是一個同步回調函數。而promise.then中回調函數纔是異步任務(微任務)。
還有一點須要注意的是,promise.then中的回調函數必須在promise狀態肯定以後才能執行。下面是一個例子:
const promise1 = new Promise(function(resolve, reject) { console.log(3); setTimeout(function(){ resolve('Success!'); console.log(1); },1000)});promise1.then(function(value) { console.log(value); console.log(2);});
//代碼輸出結果:3 (一秒後) 1 success!2
複製代碼
代碼開始運行後,先從最大的script宏任務開始,new promise直接執行,輸出3,setTimeout放入宏任務對列,緊接着的then被放入微任務隊列,而本該先執行的微任務,卻反而在宏任務結束以後才執行,這裏的執行順序顯然是不符合js的執行機制的。
如今,在這段代碼裏添加一行代碼。
const promise1 = new Promise(function(resolve, reject) { console.log(3); setTimeout(function(){ resolve('Success!'); console.log(1); },1000) resolve('haha'); //我是那行被添加的代碼});promise1.then(function(value) { console.log(value); console.log(2);});//代碼輸出結果:3 haha 2 (一秒後)1複製代碼
這時,輸出的結果就是then在前,setTimeout在後了。這行代碼改變的是promise的狀態。也就是說,then執行的前提是promise的狀態已經被肯定。
在沒加代碼前,雖然在執行機制中then已經能夠執行,可是因爲promise的狀態未肯定,致使then沒法執行(then很苦惱,到底應該執行成功的回調仍是失敗的回調呢?),沒辦法,只好等待定時器結束狀態肯定下來再執行了。加了這行代碼後,狀態已經肯定,then天然就能夠在setTimeout前執行了。而且因爲狀態一旦肯定不能更改,因此setTimeout中的resolve('Success!')已經名不副實了。