主要爲了解決回調地獄的問題html
異步結構不清晰,promise可讓異步操做結構變得很清晰面試
executor是帶有 resolve
和 reject
兩個參數的函數 。Promise構造函數執行時當即調用executor
函數, resolve
和 reject
兩個函數做爲參數傳遞給executor
(executor 函數在Promise構造函數返回所建promise實例對象前被調用)。resolve
和 reject
函數被調用時,分別將promise的狀態改成fulfilled(完成)或rejected(失敗)。executor 內部一般會執行一些異步操做,一旦異步操做執行完畢(可能成功/失敗),要麼調用resolve函數來將promise狀態改爲fulfilled,要麼調用reject
函數將promise的狀態改成rejected。若是在executor函數中拋出一個錯誤,那麼該promise 狀態爲rejected。executor函數的返回值被忽略。ajax
簡單理解上面一段話,new Promise()裏面接收一個函數,這個函數會理解執行,函數裏面有兩個參數resolve和reject,函數的執行體裏面是異步的操做,異步操做有成功有失敗數組
resolve
去接收成功的值reject
去接收失敗的值new Promise( function(resolve, reject) {...} /* executor */ );
簡單例子promise
let p1=new Promise(function(resolve,reject){ setTimeout(function(){ let num=new Date().getTime(); num%2==0?resolve('成功'):resolve('失敗') },0) }) p1.then(function(value){ console.log(value) },function(reason){ console.log(reason) })
連寫異步
let p1=new Promise(function(resolve,reject){ setTimeout(function(){ let num=new Date().getTime(); num%2==0?resolve('成功'):resolve('失敗') },0) }).then(function(value){ console.log(value) },function(reason){ console.log(reason) })
若是一個promise即調用了resolve,又調用了reject,誰先調用,最後就走對應的方法,async
new Promise(function (resolve, reject) { resolve("成功") reject("失敗") console.log("執行了") }).then(value => { console.log(value) }, reason => { console.log(reason) }) // 執行了 // 成功
若是沒有成功,剛在then的第二個參數寫的失敗的回調函數,其實也能夠用catchide
new Promise(function (resolve, reject) { reject("失敗") resolve("成功") }) .then(value => { console.log(value) }) .catch(reason => { console.log(reason) }) // 失敗
成功的語法糖函數
let p1=new Promise(function(resolve,reject){ resolve(11) }) p1.then(function(value){ console.log(value) })
Promise.resolve()ui
const p1=Promise.resolve(11); //跟上面是同樣的 p2.then(value=>{console.log(value)})
失敗的語法糖
const p3=Promise.reject(33) p2.then(null,reason=>{console.log(reason)})
Promise.All():發送了多個請求,只有所有成功才走成功的處理,只要其中有一個失敗就失敗,這個返回的是p2的緣由
const p1 = Promise.resolve(11); //跟上面是同樣的 const p2 = Promise.reject(22) const p3 = Promise.reject(33) const pAll = Promise.all([p1, p2, p3]) pAll.then( value => {}, reason => { console.log(reason) } )
const p1 = Promise.resolve(11); //跟上面是同樣的 const p2 = Promise.resolve(22) const p3 = Promise.resolve(33) const pAll = Promise.all([p1, p2, p3]) pAll.then( values => { console.log(values) } ) //[11,22,33]
多個異步任務,誰先執行完就用誰的,能夠用setTimeout延遲去模擬,這裏我就不試了
const p1 = Promise.resolve(11); //跟上面是同樣的 const p2 = Promise.resolve(22) const p3 = Promise.resolve(33) const pRace = Promise.race([p1, p2, p3]) pRace.then( value => { console.log(value) } )
若是第一個執行完是一個失敗的,那就走失敗
const p1 = Promise.reject(11); //跟上面是同樣的 const p2 = Promise.resolve(22) const p3 = Promise.resolve(33) const pRace = Promise.race([ p1,p2, p3]) pRace.then( value => { console.log(value) }, reason=>{ console.log(reason) } )
resolve(value): 若是當前是pendding就會變爲resolved reject(reason): 若是當前是pendding就會變爲rejected 拋出異常: 若是當前是pendding就會變爲rejected
//若是當前是pendding就會變爲rejected,內部拋出也是這樣 const p=new Promise((resolve,reject)=>{ throw new Error('出錯了') }) const p = new Promise((resolve, reject) =>{ //resolve(1)//promies變爲resolved成功狀態 //reject(2)//promise變爲rejected失敗狀態 //throw new Error("出錯了")//拋出異常promise變爲rejected失敗狀態,reason 爲拋出的error throw 3 }); p.then( reason => {console.log("reason:", reason)}//3 )
都會調用
const p1=Promise.resolve('11') p1.then(value=>{ console.log("第一次"+value) }) p1.then(value=>{ console.log("第二次"+value) }) //第一次11 //第二次11
3.一、都有可能,正常狀況下時先指定回調函數再改變狀態,但也能夠先改變狀態再指定回調函數 3.二、如何先改變狀態再指定回調? 3.2.一、在執行器中直接調用resolve()/reject() 3.2.二、延遲更長時間才調用then()
new Promise((resolve, reject) =>{ setTimeout(()=>{ resolve(1)//後改變的狀態(同時指定數據),異步執行回調函數 }, 1000) }).then(//先指定回調函數,保存當前指定的回調函數 value => {console.log("value1:", value)}//value1: 1 )
若是沒有返回值也沒有拋出錯誤,就走成功undefined
4.一、簡單表達:由then()指定的回調函數執行的結果決定 4.二、詳細表達: 4.2.一、若是拋出異常,新promise變爲rejected,reason爲拋出的異常 4.2.二、若是返回的是非promise的任意值,新的promise變爲resolved,value爲返回的值 4.2.三、若是返回的是另外一個新promise,此promise的結果就會成爲新promise的結果
new Promise((resolve,reject)=>{ setTimeout(function(){ resolve(11) },1000) }) .then(value=>{ console.log("第一次"+value) }) .then(value=>{ console.log("第二次"+value) }) //第一次11 //第二次undefined
new Promise((resolve,reject)=>{ setTimeout(function(){ reject(11) },1000) }) .then(value=>{ console.log("成功第一次"+value) },reason=>{ console.log("失敗第一次"+reason) }) .then(value=>{ console.log("成功第二次"+value) },reason=>{ console.log("失敗第二次"+reason) }) //失敗第一次11 // 成功第二次undefined
如下都是針對第二次then的結果
new Promise((resolve,reject)=>{ setTimeout(function(){ resolve(11) },1000) }) .then(value=>{ console.log("成功第一次"+value) // return 2 //成功第二次2 // return Promise.resolve(2) //成功第二次2 // return Promise.reject(2) //失敗第二次2 throw 3; //失敗第二次3 },reason=>{ console.log("失敗第一次"+reason) }) .then(value=>{ console.log("成功第二次"+value) },reason=>{ console.log("失敗第二次"+reason) })
一、promise的then()返回一個新的promise,能夠當作then()的鏈式調用 二、經過then的鏈式調用串連多個同步/異步任務
new Promise((resolve, reject) =>{ setTimeout(()=>{ console.log("執行任務1(異步)") resolve(1) },1000); }).then( value => { console.log("任務1的結果:", value) console.log("執行任務2(同步):") return 2 } ).then( value => { console.log("任務2的結果():", value) return new Promise((resolve, reject)=>{ setTimeout(()=>{ console.log("執行任務3(異步)") resolve(3) },1000) }) } ).then( value => { console.log("任務3的結果", value) } ) /* 執行任務1(異步) 任務1的結果: 1 執行任務2(同步): 任務2的結果(): 2 執行任務3(異步) 任務3的結果 3 */
一、當使用promise的then鏈式調用時,能夠在最後指定失敗的回調 二、前面任何操做出了異常,都會傳到最後失敗的回調中處理
第一個走的失敗的回調,可是失敗的回調沒有寫,默認他會執行 reason=>{throw reason},到第三個時,因爲第二個沒有拋出異常,也沒有返回值,因此走成功值爲undefined
catch在後面也不會成功,由於第三個走的成功,因此不會執行catch
new Promise((resolve, reject) => { reject(1) }).then( value => { console.log("onResolveed1():", value) return Promise.reject(2) } ).then( value => { console.log("onResolveed2():", value) return 3 }, reason => { console.log("第二個失敗"+reason) } ).then( value => { console.log("onResolveed3()", value) } ).catch(err=>{ console.log("catch"+err) }) /* 第二個失敗1 onResolveed3() undefined */
默認在catch後面的then函數,執行成功和失敗回調和上面的規則是同樣的
new Promise((resolve, reject) => { reject(1) }).catch(err => { console.log(err) }).then(value => { console.log("成功"+value) }, reason => { console.log("失敗"+reason) }) /* 1 成功undefined */
若是我不想執行後面then的函數呢?這就看下一個中斷promise鏈
一、當時用promise的then的鏈式調用時,在中間中斷,再也不調用後面的回調函數 二、辦法:在回調函數中返回一個pedding狀態的promise對象
new Promise((resolve, reject) => { reject(1) }).catch(err => { console.log(err) return new Promise((resolve,rejuct)=>{}) //返回一個pending的promise }).then(value => { console.log("成功"+value) }, reason => { console.log("失敗"+reason) })
一、函數的返回值爲promise對象 二、promise對象的結果由async函數執行的返回值決定
只要加了async,返回一個promise,裏面保存了狀態,若是async函數成功,下面就走成功回調,若是是失敗,就失敗的回調函數函數
async function fn1() { return 1 } let result=fn1(); console.log(result) //Promise
async function fn1() { return 1 } fn1().then(value=>{ console.log(value) }) //1
該指令會暫停異步函數的執行,並等待Promise執行,而後繼續執行異步函數,並返回結果。
一、await右側的表達式通常爲promise對象,但也能夠是其餘的值 二、若是表達式是promise對象,await返回的是promise成功的值 三、若是表達式是其餘值,直接將此值做爲await的返回值
若是value的右邊是promise,返回的是promise成功時候的值
若是value的右邊是promise,返回的是promise失敗時候的值,就用try catch來獲取
若是value右邊的不是promise,返回的是值自己
function fn2() { return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(5) },10) }) } async function fn1() { const value=await fn2(); console.log(value) } fn1() //5
注意:
await必須寫在async函數中,可是async函數中能夠沒有await
若是await的promise失敗了,就會拋出異常,須要經過try...catch來捕獲處理
一、JS中用來存儲執行回調函數的隊列包含2個不一樣特定的隊列 二、宏隊列:用來保存帶執行的宏任務,好比:定時器回調,DOM事件回調,ajax回調 三、微隊列:用來保存待執行的微任務,好比:promise的回調,MutationObserver的回調 四、JS執行時會區別這2兩個隊列 一、JS引擎首先必須先執行全部的初始化同步任務代碼 二、每次準備取出第一個宏任務前,都要將全部的微任務一個一個取出來執行
注意promise放在微任務裏面,須要更改狀態纔會放到微任務裏面,好比是從pending =》resolved
或者 pedding變爲rejected 纔會放到微隊列
setTimeout(()=>{ console.log('settimeout') },0) Promise.resolve(1).then(value=>{ console.log("promise"+value) }) /* promise1 settimeout */
js會把程序走一遍,定時器加到宏隊列,promise加到微隊列,
每次會先把微隊列執行完在執行宏隊列
setTimeout(()=>{ console.log('定時器1') },0) setTimeout(()=>{ console.log('定時器2') },0) Promise.resolve(1).then(value=>{ console.log("第一個promise"+value) }) Promise.resolve(2).then(value=>{ console.log("第二個promise"+value) }) /* 第一個promise1 第二個promise2 定時器1 定時器2 */
這裏看下是先執行 第三個promise 仍是先執行 定時器2
setTimeout(()=>{ console.log('定時器1') Promise.resolve(3).then(value=>{ console.log("第三個promise"+value) }) },0) setTimeout(()=>{ console.log('定時器2') },0) Promise.resolve(1).then(value=>{ console.log("第一個promise"+value) }) Promise.resolve(2).then(value=>{ console.log("第二個promise"+value) }) /* 第一個promise1 第二個promise2 定時器1 第三個promise3 定時器2 */
這個也很好理解,當執行第一個定時器時,就把promise添加到了微隊列
執行定時器2的時候,這個時候把微隊列的取出來執行,因此第三個promise 先執行 定時器2後執行
setTimeout(()=>{ console.log(1) },0) new Promise((resolve)=>{ console.log(2) resolve() }).then(()=>{ console.log(3) }).then(()=>{ console.log(4) }) console.log(5) //25341
第一個看同步執行 25
當執行到第一個then時,上面已經有結果了 pedding變爲resolved ,因此放到了微隊列了
這時候執行第一個then,執行完了第二個then就有狀態變化了 pedding變爲resolved,這裏也放到了微隊列
最後執行定時器
const first = () => (new Promise((resolve, reject) => { console.log(3); let p = new Promise((resolve, reject) => { console.log(7); setTimeout(() => { console.log(5); resolve(6) }, 0) resolve(1) }) resolve(2) p.then((arg) => { console.log(arg) }) })) first().then((arg) => { console.log(arg); }); console.log(4); //374125
第一步同步執行:執行第一個promise輸出3,接着執行第二個promise裏面接着輸出7,
setTimeout(() => { console.log(5); resolve(6) }, 0)
定時器放在宏隊列
往下執行
p.then((arg) => { console.log(arg) }) //這個上面執行 resolve(1)的時候就有結果了,arg的值爲1,有未成功-》成功的狀態變化,因此這個加入到微隊列裏面[then2]
resolve(2) //這句話表明了第一個promise的狀態右不成功到成功 first().then((arg) => { console.log(arg); }); //這個第一個promise的then方法的回調函數也放在隊列裏,如今隊裏 [then2(1),then2(2)]
如今執行console.log(4) ,目前執行第一遍輸出的是 3,7,4
如今取出微隊列 輸出 1,2
最後執行定時器 輸出5,那個定時器的resolve(6)沒有任何意義,由於改變了一次就不能在改變狀態了
因此隨後輸出的是3,7,4,1,2,5
記住一句話:狀態只能改變一次,因此就resolve('success1');是有效的
const promise = new Promise((resolve, reject) => { resolve('success1'); reject('error'); resolve('success2'); }); promise.then((res) => { console.log('then:', res); }).catch((err) => { console.log('catch:', err); }) //then: success1
resolve 函數將 Promise 對象的狀態從「未完成」變爲「成功」(即從 pending 變爲 resolved),在異步操做成功時調用,並將異步操做的結果,做爲參數傳遞出去; reject 函數將 Promise 對象的狀態從「未完成」變爲「失敗」(即從 pending 變爲 rejected),在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。 而一旦狀態改變,就不會再變。 因此 代碼中的reject('error'); 不會有做用。 Promise 只能 resolve 一次,剩下的調用都會被忽略。 因此 第二次的 resolve('success2'); 也不會有做用。