Promise(承諾),在程序中的意思就是承諾我過一段時間(一般是一個異步操做)後會給你一個結果,是異步編程的一種解決方案。從語法上說,原生Promise 是一個對象,從它能夠獲取異步操做的消息。javascript
promise有三種狀態 pending
(進行中) fulfilled
(已成功) rejected
(已失敗),只有異步操做的結果,才能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。java
promise只有兩種狀態改變:pending
(進行中)--> fulfilled
(已成功) ;pending
(進行中)--> rejected
(已失敗)。
當狀態改變結束時稱爲resolve
(已固定),一旦狀態變爲 resolved 後,就不能再次改變爲Fulfilled
。ios
let promise=new Promsie(function(resolve,rejec){ if(/*異步執行成功*/){ resolve(value); }else{ reject(error); } }) promise.then(function(){ //回調執行成功以後的操做 },function(){ //回調執行失敗以後的操做,可選 });
Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由 JavaScript 引擎提供。當異步操做成功時(pending--fulfilled
),調用resolve(value)
函數把操做結果當成參數傳出,當異步操做成功時(pending--rejected
)調用 reject(error)
函數把錯誤返回。Promise實例生成之後,用then
方法分別指定resolved狀態和rejected狀態的回調函數。es6
Promise.prototype.then()
做用是爲 Promise 實例添加狀態改變時的回調函數。接受兩個回調函數做爲參數。第一個回調函數是Promise對象的狀態變爲resolved時調用,第二個回調函數是Promise對象的狀態變爲rejected時調用。其中,第二個函數是可選的,不必定要提供。Promise.prototype.then()
返回的是另外一個Promise對象,後面還能夠接着調用then方法。Promise.prototype.catch()
則是.then(null, rejection)
或.then(undefined, rejection)
的別名,用於指定發生錯誤時的回調函數。 Promise 對象的錯誤具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤老是會被下一個catch語句捕獲。Promise.catch()
方法返回的也是一個 Promise 對象,所以後面還能夠接着調用then方法。上述代碼也能夠理解成這樣:ajax
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 處理 getJSON 和 前一個回調函數運行時發生的錯誤 console.log('發生錯誤!', error); });
finally
方法用於指定無論 Promise 對象最後狀態如何,都會執行的回調函數。該方法是 ES2018 引入標準的。finally
方法的回調函數不接受任何參數,這意味着沒有辦法知道,前面的 Promise 狀態究竟是fulfilled仍是rejected。這代表,finally
方法裏面的操做,應該是與狀態無關的,不依賴於 Promise 的執行結果。finally
本質上是then
方法的特例。編程
promise.then(()=>{}).catch(()=>{}).finally(() => { // 操做 }); // 等同於 promise.then(result => { // 操做 return result; }).catch( error => { // 操做 throw error; });
.then
每次調用返回的都是一個新的Promise實例,若是then中返回的是一個結果的話會把這個結果傳遞下一次then中的成功回調,因此能夠鏈式調用該實例。在 then中使用了return,那麼 return 的值會被Promise.resolve()
包裝,then中也能夠不傳遞參數,若是不傳遞會透到下一個then中。json
Promise.resolve(1).then(res => { console.log(res) return 2 //包裝成 Promise.resolve(2) }).catch(err => 3).then().then(res => console.log(res))
將現有的對象轉換(包裝)成 promise對象。四種參數類型:axios
不帶參數傳遞 --- 返回一個新的狀態爲resolve的promise對象。數組
let p = Priomse.resolve() // p就是promise
參數是帶then方法的對象promise
let data = { then:function(resolve,reject){ resovle('帶then方法的對象') } } Promise.resolve(data).thne((res)=> console.log(res)) // '帶then方法的對象'
返回一個新的promise,並直接執行then
的方法,promise對象的狀態就變爲resolved
,從而當即執行最後那個then方法指定的回調函數,輸出 '帶then方法的對象'
。
參數是非空,非then方法的對象,非proimse的
let p = Promise.resolve('foo') // 等價於 let p = new Promise(resolve => resolve('foo')) p.then(res=>console.log(res)) //'foo'
返回一個新的狀態爲resolve的promise對象,因此then
回調函數會當即執行。Promise.resolve
方法的參數,會同時傳給回調函數。
參數爲非then對象時-----Promise.reject(reason)
方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected
let p = Promise.reject('error') // 等價於 let p = new Primose((resolve,reject)=>reject('出錯了')}) //處理錯誤的回調 p.then((null,res)=>console.log(res)) //'出錯了'
參數是帶then方法的對象 ---返回的並非then
方法的回調函數,而是data對象自己
let data = { then:function(resolve,reject){ reject('帶then方法的對象出錯了') } } Promise.resolve(data).thne((null,res)=> console.log(res)) // data //等同於 Promise.resolve(data).catch(res=> console.log(res)) // data
該方法將多個promise實例,包裝成一個新的promise實例。
let p = Promise.all([p1,p2,p3])
參數不必定爲數組,但必須爲一個可迭代Iterator
,且返回的每一個成員(p1,p2,p3)都是 Promise 實例,若是不是,就會先調用的Promise.resolve
方法,將參數轉爲 Promise 實例,再進一步處理。
var p = Promise.all([1,2,3]); var p2 = Promise.all([1,2,3, Promise.resolve(444)]); var p3 = Promise.all([1,2,3, Promise.reject(555)]); setTimeout(function() { console.log(p);// Promise { <state>: "fulfilled", <value>: Array[3] } console.log(p2); // Promise { <state>: "fulfilled", <value>: Array[4] } console.log(p3); // Promise { <state>: "rejected", <reason>: 555 } }); p.then(function (posts) { // ..當有返回值的時候纔會回調 }).catch(function(reason){ // ... });
p1
,p2
,p3
中得實例都改變成 fulfilled
(已成功)時,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。p1
,p2
,p3
中得實例其中一項的改變成 rejected
(已失敗)時,p的狀態就變成rejected
,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。Promise.all()是異步解析,只有這當全部實例的狀態都變成fulfilled
,或者其中有一個變爲rejected
,纔會調用Promise.all方法後面的回調函數then
,catch
方法。可是當且僅當傳遞的iterable爲空時,Promise.all纔會同步解析。
var p = Promise.all([]); console.log(p);//Promise { <state>: "fulfilled", <value>: Array[0] }
處理錯誤,常規狀況下,當其中一個實例返回rejected
,就會調用Promise.all
的catch
方法,返回第一個錯誤。但實際應用時,咱們想讓全部的實例不論成功或失敗就能夠返回參數組成數組,這時就能夠調用實例自身的catch
方法來規避這種狀況。
const p1 = new Promise((resolve, reject) => { resolve('hello'); //resolved }).then(result => result).catch(e => e); const p2 = new Promise((resolve, reject) => { throw new Error('報錯了');//rejected }).then(result => result).catch(e => e); Promise.all([p1, p2]) .then(result => console.log(result))// ["hello", Error: 報錯了] .catch(e => console.log(e));
p1會resolved
,p2首先會rejected
,可是p2有本身的catch方法,該方法返回的是一個新的 Promise 實例,p2指向的其實是這個實例。該實例執行完catch方法後,也會變成resolved,致使Promise.all()方法參數裏面的兩個實例都會resolved,所以會調用then方法指定的回調函數,而不會調用catch方法指定的回調函數。
js原生實現Promise.all的原理
//在Promise類上添加一個all方法,接受一個傳進來的promise數組 Promise.all = function (promiseArrs) { return new Promise((resolve, reject) => { //返回一個新的Promise let arr = []; //定義一個空數組存放結果 let i = 0; function handleData(index, data) { //處理數據函數 arr[index] = data; i++; if (i === promiseArrs.length) { //當i等於傳遞的數組的長度時 resolve(arr); //執行resolve,並將結果放入 } } for (let i = 0; i < promiseArrs.length; i++) { //循環遍歷數組 promiseArrs[i].then((data) => { handleData(i, data); //將結果和索引傳入handleData函數 }, reject) } }) }
若是說all體驗很差,那咱們也能夠本身作一個some方法,表示所有失敗纔算失敗
Promise.some = function (promiseArrs) { return new Promise((resolve, reject) => { let arr = []; //定義一個空數組存放結果 let i = 0; function handleErr(index, err) { //處理錯誤函數 arr[index] = err; i++; if (i === promiseArrs.length) { //當i等於傳遞的數組的長度時 reject(err); //執行reject,並將結果放入 } } for (let i = 0; i < promiseArrs.length; i++) { //循環遍歷數組 promiseArrs[i].then(resolve, (e) => handleErr(i, e)) } }) }
該方法和promise.all相似,就是解決all方法在處理錯誤時的不合理而出現的。其參數接受一個Promise的數組, 返回一個新的Promise, 惟一與all的不一樣在於, 其不會進行短路, 也就是說當Promise所有處理完成後咱們能夠拿到每一個Promise的狀態, 而無論其是否處理成功。
和all相似,當自身實例有catch回調時,每一個實例狀態變爲fulfilled
const p3 = new Promise((resolve, reject) => { resolve('hello'); //resolved }).then(result => result).catch(e => e); const p4 = new Promise((resolve, reject) => { throw new Error('報錯了');//rejected }).then(result => result).catch(e => e); Promise.allSettled([p3, p4]) .then(result => console.log(result)) .catch(e => console.log(e)); //.then的log //[{status: "fulfilled", value: "hello"},{status: "fulfilled", reason: Error: 報錯了 at <anonymous>:6:10 at new Promise (<anonymous>) at <anonymous>:5:13}]
沒有catch接收錯誤,返回自身的狀態和回調參數
const p5 = new Promise((resolve, reject) => { resolve('hello'); //resolved }).then(result => result) const p6 = new Promise((resolve, reject) => { throw new Error('報錯了');//rejected }).then(result => result) Promise.allSettled([p5, p6]) .then(result => console.log(result)) .catch(e => console.log(e)); //.then的log //[{status: "fulfilled", value: "hello"},{status: "rejected", reason: Error: 報錯了 at <anonymous>:6:10 at new Promise (<anonymous>) at <anonymous>:5:13}]
該方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例,其餘特色和all很像,和all的區別在於:race方法比如是賽跑,幾個實例一塊兒跑,誰先到就成功了,就resolve誰,或者誰跑到中途摔了出現異常情況失敗了,就reject誰,不論成功仍是失敗,就先捕獲第一個完成的。
捕獲第一個成功的實例回調函數
let p1 = Promise.resolve('1') let p2 = Promise.resolve('2') Promise.race([p1,p2]).then(res=>conseloe.log(res))// '1'
捕獲第一個結果
let p1 = Promise.resolve("1"); let p2 = Promise.reject("ERR2"); Promise.race([p1,p2]).then(res=>conseloe.log(res)) //Promise {<resolved>: "1"}
捕獲第一個錯誤
let p1 = Promise.reject("ERR1"); let p2 = Promise.reject("ERR2"); Promise.race([p1,p2]).catch(console.log) //Promise {<reject>: "ERR1"}
原生實現Promise.race()的設計原理
Promise._race = iterator =>{ return new Promise((resolve,reject)=>{ iterator.forEach(item=>{ Promise.resolve(item).then(resolve).catch(reject) }) }) }
在實際開發使用promise時,但願通過promise包裝後的函數內部代碼讓同步函數同步執行,異步函數異步執行,而且讓它們具備統一的 API
例:當同步函數被promise包裝後的執行順序改變。
let fn = () =>console.log('同步1'); Promise.resolve().then(fn) console.log('同步2') //log後 //'同步2' //'同步1'
Promise.try的應用
該方法是用來模擬try
的代碼塊的,就像promise.catch
模擬的是catch
代碼塊。
理解 try catch finally
try catch是JavaScript的異常處理機制,把可能出錯的代碼放在try語句塊中,若是出錯了,就會被catch捕獲來處理異常。若是不catch 一旦出錯就會形成程序崩潰。finally:不管結果如何,容許在 try 和 catch 以後執行代碼。
try { // 供測試的代碼塊 } catch(err) { //處理錯誤的代碼塊 } finally { //不管 try / catch 結果如何都執行的代碼塊 }
應用
let fn = () => console.log('同步1'); Promise.try(fn); console.log('同步2'); //'同步1' //'同步2'
方法一:使用async匿名函數,會當即執行裏面的async函數,所以若是f是同步的,就會獲得同步的結果;若是f是異步的,就能夠用then指定下一步,若是想捕獲錯誤,使用catch方法。
let fn = () =>console.log('同步1'); (async ()=>fn())() .then(resolve) .catch(err=>console.log(err)) console.log('同步2') //log後 //'同步1' //'同步2'
方法二:使用promise當即執行的匿名函數
let fn = () =>console.log('同步1'); ( () => new Promise( resolve => resolve(fn()) ))() console.log('同步2') //log後 //'同步1' //'同步2'
over~有問題留言
拓展:
借鑑:
https://blog.csdn.net/sjw1039...
http://es6.ruanyifeng.com/#do...
https://developer.mozilla.org...