只有異步操做的結果才能改變狀態,這也是Promise承諾,名字的由來
resolved已定型
可是通常resolved都是特指fulfilled狀態,而不包括rejected狀態
一旦新建就會當即執行
,沒法中途取消 (2) 若是不設置回調函數,那麼Promise內部拋出的錯誤不會反映到外部
(3) 當處於pending狀態時,沒法得知目前發展到哪一個階段剛剛開始仍是即將完成
new Promise()的參數函數中有兩個參數,調用第一個參數函數會改變狀態爲resolved狀態,調用第二個會改變狀態爲rejected狀態
第一個參數函數在resolved狀態時調用,第二個參數在rejected狀態時調用
var p=new Promise(function(res,rej){ console.log("剛剛new Promise()就會執行,即便是賦值操做") // res();// 狀態改成resolved rej();// 狀態改成rejected }) // 獲取狀態 p.then(()=>{ console.log('狀態改變爲resolved') },()=>{ console.log('狀態改變爲rejected') }) 複製代碼
function pro(time){ return new Promise((res,rej)=>{ // new Promise以後就執行,res會在time時間後執行,從而狀態改變爲resolved setTimeout(res,time) }) } console.log(Math.floor(new Date().getTime()/1000)) pro(1000).then(()=>{ console.log(Math.floor(new Date().getTime()/1000)) console.log("在1000ms後執行") }) 複製代碼
let p=new Promise(function(res,rej){ console.log("new Promise代碼執行,會當即執行該步驟") console.log(1); res() }) p.then(()=>{ console.log("then操做時異步操做,會在本輪宏任務結束以前才執行") console.log(3) }) console.log(2); 複製代碼
var get=function(url){ return new Promise((res,rej)=>{ var xhr=new XMLHttpRequest(); xhr.open("GET",url); xhr.onreadystatechange=function(){ if(this.readyState==4&&this.status==200){ res(this.response) } } xhr.onerror=(err)=>{ rej(err) } xhr.send(); }) } // http://127.0.0.1:8849,此時會報錯(由於本地沒開啓該端口) get("http://127.0.0.1:8848/MyHxsj/面經.md").then((data)=>{ console.log(data) },(err)=>{ console.log(err) }) 複製代碼
resolve函數的參數是一個Promise實例時,返回的實例的狀態決定了當前Promise實例的狀態
若是返回的Promise實例的狀態是pending,那麼當前Promise實例會等待pending狀態改變
若是返回的Promise實例的狀態是resolved或者rejected,那麼回調函數就會當即執行
// 1. 返回的Promise實例的狀態爲pending var p1=new Promise(function(res,rej){ console.log("pending") setTimeout(function(){ res("resolved") },2000) }) var p2=new Promise(function(res,rej){ res(p1);// resolved狀態,可是傳遞的參數爲Promise實例 }) p2.then((data)=>{ console.log(data) console.log("在返回的Promise實例p1狀態爲resolved以後執行") }) /* pending // 兩秒以後 resolved 在返回的Promise實例p1狀態爲resolved以後執行 */ // 2. 返回的Promise實例的狀態爲resolved var p3=new Promise(function(res){ res("p3") }) var p4=new Promise(function(res,rej){ res(p3) }) p4.then((res)=>{ console.log(res);//p3,當即執行 }) 複製代碼
promise實例改變狀態爲resolve不會改變同步代碼的執行
// 1. resolved new Promise((res,rej)=>{ res(1); console.log("此前狀態爲resolved,可是依舊會執行本行代碼") }).then((data)=>{ console.log(data) }) console.log('宏任務代碼') /* 此前狀態爲resolved,可是依舊會執行本行代碼 宏任務代碼 1 */ // 2. rejected new Promise((res,rej)=>{ rej(new Error("錯誤error:")); console.log("此時狀態爲rejected,本行代碼會執行") }).then(()=>{},(err)=>{ console.log(err) }) /* 此時狀態爲rejected,本行代碼會執行 // 而後纔會輪到微任務執行 Error: 錯誤error: at 平時測試.html:130 at new Promise (<anonymous>) at 平時測試.html:129 */ 複製代碼
// 若是想要在改變狀態爲resolved或者rejected以後 // 不會繼續執行往下代碼,那麼使用return new Promise(function(res,rej){ return res(1) // return rej(1) console.log("已經return,因此不會繼續執行本行代碼") }).then((data)=>{ console.log(data) },(err)=>{ console.log(err) }) // 結果只會打印: 1 複製代碼
then方法的參數不是函數時會發生穿透。也就是then(1).then((data)=>{}),第二個then得到的參數函數的data就是上次穿透的數據1
若是promise實例存在多個then,那麼下一個then會等待上一個then返回的promise實例狀態改成resolved/rejected纔會執行
// 多個then,須要等待上一個then的狀態改成resolved/rejected new Promise((res,rej)=>{ res(1) }).then((res)=>{ console.log(res); return new Promise((res,rej)=>{ setTimeout(res,1000);// 一秒後,狀態改成resolve }) }).then(()=>{ console.log(2) }) /* 1 // 1秒後 2 */ 複製代碼
new Promise((res,rej)=>{ res(1) }).then(2).then((data)=>{ console.log(data);//1 return 3; }).then((data)=>{ console.log(data)//3 }) /* res(1),函數res傳遞的是一個number類型的變量 then(2),不是函數,沒有傳遞值,因此無效,僅僅是一個語句 2 // 因此1的值會繼續穿透,then(2)無效 1,第二個then屬於函數對象,因此可以接收到數據1 return 3,傳遞數據3 最後一個then,打印3,接收到上個then函數返回的數據3 */ 複製代碼
Promise.prototype.catch()方法相等於Promise.prototype.then(null,reject())或者Promise.prototype.then(undefined,reject())
調用完catch方法,狀態會變爲resolved!
// 1. 捕獲Promise錯誤 new Promise(function(res,rej){ throw new Error("拋出錯誤") }).catch(function(err){ console.log("err:",err) }) /* err: Error: 拋出錯誤 at 平時測試.html:116 at new Promise (<anonymous>) at 平時測試.html:115 */ // 2. then方法中拋出錯誤 new Promise(function(res,rej){ res(1) }).then(function(data){ throw new Error("then err") }).catch(function(err){ console.log(err) }) /* Error: then err at 平時測試.html:131 */ 複製代碼
在promise的後面使用catch其實至關於在promise內部加上try-catch語句,或者至關於加上一個reject狀態改變(在某個條件符合時)
// 1. catch方法相等於try-catch語句 new Promise(function(res,rej){ try{ throw new Error("try拋出錯誤") }catch(e){ console.log(e) } }) /* Error: try拋出錯誤 at 平時測試.html:117 at new Promise (<anonymous>) at 平時測試.html:115 */ // 2. catch方法還相等於調用reject方法而後在then的第二個參數監聽 new Promise(function(res,rej){ rej("rej err") }).then(()=>{},(err)=>{ console.log(err) });//rej err 複製代碼
因爲catch至關於調用rejected狀態,而且狀態改成rejected後不能夠改成resolve狀態
因此Promise狀態爲resolve時,再拋出錯誤,沒法在catch捕獲到
// 1. res(),resolve狀態後不能夠改成reject狀態 new Promise(function(res,rej){ res("ok");//狀態爲resolve throw new Error("此時拋出錯誤沒法被捕獲到") }).catch(function(err){ console.log(err) }).then((data)=>{ console.log(data);//ok }) 複製代碼
new Promise(function(res,rej){ throw new Error("err") }).then(()=>{},(err)=>{ console.log("reject:",err) }).catch(function(data){ console.log('catch:',data) }) /* reject: Error: err at 平時測試.html:116 at new Promise (<anonymous>) at 平時測試.html:115 */ 複製代碼
// 1. new Promise內部會拋出錯誤 new Promise(function(res,rej){ throw new Error("err") }) // 2.即便上面拋出了錯誤,剩餘的代碼依舊會執行 // 也就是Promise內部拋出的錯誤不會影響到外部 setTimeout(()=>{ console.log("繼續執行") },1000) // 會打印錯誤,以後打印 "繼續執行" 複製代碼
try執行以前,try執行過程當中,try執行以後
// 1.語法錯誤,在執行try代碼以前就拋出錯誤,因此catch沒法捕獲到 /* try{ a. }catch(e){ console.log("myerr:",e) } */ /* Uncaught SyntaxError: Unexpected token '}' */ // 2. 在try代碼執行過程當中拋出的錯誤(能夠捕獲到) /* function add(x){return x+y}; // 錯誤函數 try{ console.log(add(1)) }catch(e){ console.log(e);//ReferenceError: y is not defined } */ // 3. 在try代碼執行完畢以後拋出的錯誤(不能捕獲) /* try{ setTimeout(function(){ console.log('定時器') // 此時的錯誤沒有被catch捕獲到 a.b;//Uncaught ReferenceError: a is not defined },2000) }catch(e){ console.log("捕獲到的錯誤:",e); } */ // 4.try/catch沒法捕獲到Promise異常時由於Promise內部就捕獲到異常了,沒有往外拋出異常,因此外部的catch沒法捕獲到異常 try{ new Promise(function(res,rej){ // 當沒有在Promise中設置catch時,提示下面語句 throw new Error("err");//Uncaught SyntaxError: Missing catch or finally after try }).then(()=>{},(err)=>{ console.log("第二個參數:",err) /* 第二個參數: Error: err at 平時測試.html:147 at new Promise (<anonymous>) at 平時測試.html:145 */ }) .catch(function(err){ console.log("catch:",err) /* 沒有設置then第二個參數函數時 catch: Error: err at 平時測試.html:147 at new Promise (<anonymous>) at 平時測試.html:145 */ }) }catch(e){ console.log("promise:",e);// 沒法捕獲到promise內部錯誤 } 複製代碼
因此catch後面接上的then能夠繼續執行
catch中也能夠繼續拋出錯誤,而後被後面的then或者catch繼續捕獲
// 1. catch捕獲拋出的錯誤後,then方法能夠繼續執行 new Promise(function(){ throw new Error("err") }).catch(function(err){ console.log(err) }).then(function(){ console.log("catch捕獲錯誤後,還能夠繼續執行then") }) /* Error: err at 平時測試.html:116 at new Promise (<anonymous>) at 平時測試.html:115 catch捕獲錯誤後,還能夠繼續執行then */ // 2. catch中也能夠拋出錯誤,而後被下一個catch捕獲 new Promise(function(res,rej){ throw new Error("第一個錯誤") }).catch(function(err){ console.log(err) throw new Error("第二個錯誤") }).catch(function(err){ console.log(err) }) /* Error: 第一個錯誤 at 平時測試.html:133 at new Promise (<anonymous>) at 平時測試.html:132 Error: 第二個錯誤 at 平時測試.html:136 */ 複製代碼
ES2018才引入的,就是無論promise的狀態是什麼,最後都會執行
finally方法的回調函數不能接受任何參數,這就意味着不能知道前面的Promise狀態究竟是fulfilled仍是rejected
而且finally方法老是會返回原來的值
// 1. finally在最後 new Promise(function(res,rej){ res(1) }).then((data)=>{ console.log(data);//1 return 2 }).finally(()=>{ console.log(3);//3, finally方法不能接受到參數,因此2接收1不到 }) /* 1 3 */ // 2. finally後面還有then方法 new Promise((res,rej)=>{ res(11) }).then((data)=>{ console.log(data);//11 return 22; }).finally(()=>{ console.log(33);//33 }).then((data)=>{ console.log(data);//22,執行上一個被返回的then }) // 3. finally後面還有then,而且finally返回值 new Promise((res,rej)=>{ res(111) }).then((data)=>{ console.log(data);//111 return "後續then接收到以前的then,而不會接收到finally return的變量" }).finally(()=>{ console.log(222);//222 return 333; }).then((data)=>{ console.log(data);//後續then接收到以前的then,而不會接收到finally return的變量 }) 複製代碼
Promise.all()接收一個具備Iterator接口的數據做爲參數,關鍵在於成員必須都是Promise實例
成員都變爲resolved以後會返回結果數組;若是有一個成員變爲rejected狀態,那麼返回rejected狀態Promise實例的返回值
// 1. 所有resolved var p1=new Promise((res,rej)=>{ res(1); }) var p2=new Promise((res,rej)=>{ res(2); }) var p3=new Promise((res,rej)=>{ res(3); }) // Promise.all()返回的是一個Promise實例 // 該實例的值存儲在內部屬性 [[PromiseValue]] // 想要獲取,須要經過then方法來保存參數 var res1=Promise.all([p1,p2,p3]) .then((data)=>{ console.log(data);//[1, 2, 3] return data; }) console.log(res1);//Promise {<pending>} // 2. 有一個rejected,其餘的中止執行 var p4=new Promise((res,rej)=>{ console.log("p4") setTimeout(res(4),5000); }) // 2.1 錯誤沒被捕獲 var p5=new Promise((res,rej)=>{ setTimeout(rej("error:"+5555),1000) }) var p6=new Promise((res,rej)=>{ console.log("p6") res(6) }) /* var res2=Promise.all([p4,p5,p6]).then((data)=>{ console.log("all:",data); }) */ // 2.2 /* var p5=new Promise((res,rej)=>{ setTimeout(rej("error:"+5555),1000) }).catch(function(err){ console.log("err",err);//err error:5555 }).then((res)=>{ return "res" }) console.log(p5) */ // 2.3 給Promise.all後面添加catch捕獲錯誤 var res2=Promise.all([p4,p5,p6]).then((data)=>{ console.log("all:",data); }).catch((err)=>{ console.log("all+err",err);//all+err error:5555 }) /* 2.1 沒有給p5設置catch捕獲錯誤,因此最後執行錯誤,Promise.all沒接住錯誤 2.2 給p5設置catch捕獲錯誤,則Promise.all剩餘實例也會執行,可是返回rej的實例的值多是undefined 也就是Promise.all的結果是[4,undefined,6] 2.3 給Promise.all後面添加catch捕獲錯誤,打印all+err error:5555 2.4 若是實例和Promise.all都設置了catch,那麼使用實例的catch來捕獲錯誤,由於實例離得近 */ 複製代碼
只捕獲最先的那個錯誤
function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p } function runReject (x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)) return p } Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]) .then(res =>console.log(res)) .then(res =>console.log(res),rej=> console.log('error:',rej)) .catch(err =>console.log(err)) 複製代碼
多個異步任務是並行執行的,哪一個先執行完畢不必定
當all方法出現異常時,all以後的then接收的是reject函數,參數就是第一個異常返回的參數
當all方法以後的then不存在reject函數時,則catch接受異常參數
即便不是all以後的第一個then存在reject函數,只要以後的then存在reject函數,就不會輪到catch去接收異常
若是異步任務中存在報錯,那麼res不會執行,rej纔會執行,也就是報錯
若是不存在報錯,那麼就會執行res
只保留第一個執行結果,剩下的異步任務仍在執行,可是執行結果沒法獲取
其餘異步任務依舊會執行
function runAsync(x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000) ); return p; } function runReject(x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x) ); return p; } /* 首先打印0,而後返回rej('Error:0') 該rej被Promise.race的catch捕獲,可是剩下的成員依舊執行 因此runAsync還會打印 1 2 3 */ Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)]) .then(res =>console.log("result: ", res)) .catch(err =>console.log(err));// Error:0 複製代碼
將現有的對象轉換爲resolve狀態的異步對象就使用Promise.resolve()
// 1. 使用Promise.resolve() var res1=Promise.resolve("1"); console.log(res1);//Promise {<resolved>: "1"} // 2. 等同於 new Promise((res,rej)=> res()) var res2=new Promise((res,rej)=>{ res("2") }) console.log(res2);//Promise {<resolved>: "2"} 複製代碼
那麼就至關於Promise.resolve啥都沒作,無效
注意thenable對象須要有參數res/rej,而後執行res(xxx)用於傳遞數據
var a={ then:function(res){ res(444) } } Promise.resolve(a).then((data)=>{ console.log(data);//444 }) 複製代碼
此時直接傳遞該參數
// 1. 參數不是對象 Promise.resolve('hello world').then((data)=>{ console.log(data);//hello world }) // 2. 參數是對象,可是沒有then方法 Promise.resolve({a:'hello'}).then((data)=>{ console.log(data);//{a: "hello"} }) 複製代碼
不帶參數就then接收不到數據,仍是可使用!
與Promise.resolve()不一樣之處在於,reject不管參數爲何,都是直接返回該參數!
注意要使用catch接收,不然會報錯,由於是reject狀態
// 1. thenable對象,直接返回參數 var obj={ then:function(res,rej){ rej("err") } } Promise.reject(obj).catch((e)=>{ console.log(e);//{then: ƒ} }) //2. 其餘參數 Promise.reject("he").catch((res)=>{ console.log(res);// he }) 複製代碼
function mypromise(func){ var that=this; // 回調函數集 that.funclist=[]; // resolve方法 function resolve(value){ // 微任務 setTimeout(()=>{ that.data=value that.funclist.forEach((callback)=>{ callback(value) }) }) } // 執行用戶傳入的函數 func(resolve.bind(that)) } mypromise.prototype.then=function(onResolved){ var that=this; return new mypromise(resolve => { that.funclist.push(function(){ var res=onResolved(that.data); if(res instanceof mypromise){ res.then(resolve) }else{ resolve(res); } }) }) } const func=resolve => { setTimeout(()=>{ resolve(1) },1000) } var promise1 = new mypromise(func) promise1.then(res => { console.log(res) return new mypromise(resolve => { setTimeout(() => { resolve(2) }, 500) }) }) console.log(promise1); 複製代碼
// 用於預加載圖片 function loadImg(url){ return new Promise(function(res,rej){ var img=document.createElement('img'); img.src=url; // 設置了src,也就是默認開始請求圖片資源了 img.onload=res;//指向函數res img.onerror=rej; }) } console.log(loadImg("http://yiyeblog.com/imgs/ES6.png")) 複製代碼
本文使用 mdnice 排版html