Promise知識(shí)點(diǎn)總結(jié)

Promise 是什么?

Promise是ES6語法,是JS中解決異步編程的新解決方案。(舊的解決方案是單純的調(diào)用回調(diào)函數(shù))

從語法上來說,Promise是一個(gè)構(gòu)造函數(shù)

從功能上來說,Promise對(duì)象用來封裝一個(gè)異步操作,并可以獲取其成功或者失敗的結(jié)果值

異步編程包括但不限于以下幾個(gè):

  • fs 文件操作
  • 數(shù)據(jù)庫操作
  • Ajax
  • 定時(shí)器

為什么要用Promise?

Promise支持鏈?zhǔn)秸{(diào)用,解決回調(diào)地獄問題

在 Promise 出現(xiàn)以前,我們處理一個(gè)異步網(wǎng)絡(luò)請求,大概是這樣:

// 請求 代表 一個(gè)異步網(wǎng)絡(luò)調(diào)用。
// 請求結(jié)果 代表網(wǎng)絡(luò)請求的響應(yīng)。
請求1(function(請求結(jié)果1){
    處理請求結(jié)果1
})

看起來還不錯(cuò)。

但是,需求變化了,我們需要根據(jù)第一個(gè)網(wǎng)絡(luò)請求的結(jié)果,再去執(zhí)行第二個(gè)網(wǎng)絡(luò)請求,代碼大概如下:

請求1(function(請求結(jié)果1){
    請求2(function(請求結(jié)果2){
        處理請求結(jié)果2
    })
})

看起來也不復(fù)雜。

但是。。需求是永無止境的,于是乎出現(xiàn)了如下的代碼:

請求1(function(請求結(jié)果1){
    請求2(function(請求結(jié)果2){
        請求3(function(請求結(jié)果3){
            請求4(function(請求結(jié)果4){
                請求5(function(請求結(jié)果5){
                    請求6(function(請求結(jié)果3){
                        ...
                    })
                })
            })
        })
    })
})

這就出現(xiàn)了所謂的回調(diào)地獄(回調(diào)函數(shù)嵌套調(diào)用,外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)執(zhí)行的條件)

回調(diào)地獄帶來的負(fù)面作用有以下幾點(diǎn):

  • 代碼臃腫。
  • 可讀性差。
  • 耦合度過高,可維護(hù)性差。
  • 代碼復(fù)用性差。
  • 容易滋生 bug。
  • 只能在回調(diào)里處理異常。

解決方案: Promise鏈?zhǔn)秸{(diào)用

Promise初體驗(yàn)

// resolve 和 reject 都是函數(shù)類型的數(shù)據(jù)
const p = new Promise ((resolve, reject) => {
  let status = true;
  setTimeout(() => {
    if(status) {
      resolve('正確信息')
    } else {
      reject('錯(cuò)誤信息')
    }
  },2000)
})

p.then((data) => {
  console.log(data);
})

Promise狀態(tài)改變

Promise 對(duì)象有三個(gè)狀態(tài),并且狀態(tài)一旦改變,便不能再被更改為其他狀態(tài)。

這個(gè)狀態(tài)實(shí)際上是Promise實(shí)例對(duì)象中的一個(gè)屬性[PromiseState]

實(shí)例對(duì)象中的另一個(gè)重要的屬性 PromiseResult,存的是對(duì)象成功或者失敗的結(jié)果

  • pending,異步任務(wù)正在進(jìn)行。(未決定的)
  • resolved (也可以叫fulfilled),異步任務(wù)執(zhí)行成功。
  • rejected,異步任務(wù)執(zhí)行失敗。

Promise狀態(tài)改變只有 pending 變?yōu)?resolvedpending 變?yōu)?rejected 這兩種

且一個(gè) Promise 對(duì)象只能改變一次,無論成功或者失敗都會(huì)有一個(gè)結(jié)果數(shù)據(jù),成功的結(jié)果數(shù)據(jù)一般稱為 value,失敗的結(jié)果數(shù)據(jù)一般稱為 reason。

Promise的API

1. Promise構(gòu)造函數(shù)

  • executor函數(shù):執(zhí)行器
  • resolve函數(shù):內(nèi)部定義成功時(shí)我們執(zhí)行的函數(shù)
  • reject函數(shù):內(nèi)部定義失敗時(shí)我們執(zhí)行的函數(shù)

說明executor 會(huì)再 Promise 內(nèi)部立即同步調(diào)用,異步操作在執(zhí)行器中執(zhí)行。

舉例說明:

const p = new Promise((resolve, reject) => {
  console.log('222')
})

console.log('111')
// 打印結(jié)果為: 111  222 , 從而證明了上面的結(jié)論

2. Promise.prototype.then()

語法:

p.then(onResolved[, onRejected]);

p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

指定用于得到成功 value 的成功回調(diào)和用于得到失敗 reason 的失敗回調(diào),返回一個(gè)新的 promise 對(duì)象。

3. Promise.prototype.catch()

catch() 方法返回一個(gè) Promise,并且處理拒絕的情況。它的行為與調(diào)用 Promise.prototype.then(undefined, onRejected) 相同。他只能用來指定失敗的回調(diào)

同樣支持鏈?zhǔn)秸{(diào)用,本質(zhì)上其實(shí)是then()方法的一個(gè)單獨(dú)封裝

語法:

p.catch(onRejected);

p.catch(function(reason) {
   // 拒絕
});

3. Promise.resolve() 方法

這個(gè)方法比較特殊,它是屬于 Promise 這個(gè)函數(shù)對(duì)象的,并不屬于實(shí)例對(duì)象,也就是說它是靜態(tài)成員。

用法:接收一個(gè)參數(shù),返回一個(gè)promise成功或者失敗對(duì)象

語法:Promise.resolve(value);

// 如果傳入的參數(shù)為非 promise 類型的對(duì)象,則返回的結(jié)果為成功的promise對(duì)象
let p1 = Promise.resolve(123)
console.log(p1)

// 如果傳入的參數(shù)為 promise 對(duì)象,則參數(shù)的結(jié)果決定了 resolve 的結(jié)果,比如:
let p2 = Promise.resolve(new Promise((resolve, reject) => {
  resolve('ok')
}))

console.log(p2) // 因?yàn)閰?shù)是promise對(duì)象,且返回的結(jié)果是成功的,值為ok,那么此時(shí)p2的結(jié)果也是成功的,值同樣為ok

4. Promise.reject() 方法

這個(gè)方法和上面的 Promise.resolve 方法一樣,是屬于 Promise 這個(gè)函數(shù)對(duì)象的,不屬于實(shí)例對(duì)象,同樣為靜態(tài)成員。

用法:快速返回一個(gè)帶有拒絕原因的promise對(duì)象

語法:Promise.reject(reason)

let p1 = Promise.reject(123)

let p2 = Promise.reject(new Promise((resolve, reject) => {
  resolve('成功')
}))

console.log(p1)
console.log(p2)

// 無論你傳入?yún)?shù)是不是promise對(duì)象,都會(huì)返回一個(gè)被拒絕的promise對(duì)象,對(duì)調(diào)試和選擇性錯(cuò)誤捕捉很有幫助

Promise.all()

這個(gè)方法也是屬于 Promise 函數(shù)對(duì)象的,不屬于實(shí)例對(duì)象。

Promise.all() 方法接收一個(gè)promise的 iterable 類型(注:Array,Map,Set都屬于ES6的iterable類型)的輸入,并且只返回一個(gè)Promise實(shí)例, 那個(gè)輸入的所有promise的resolve回調(diào)的結(jié)果是一個(gè)數(shù)組。這個(gè)Promise的resolve回調(diào)執(zhí)行是在所有輸入的promise的resolve回調(diào)都結(jié)束,或者輸入的iterable里沒有promise了的時(shí)候。它的reject回調(diào)執(zhí)行是,只要任何一個(gè)輸入的promise的reject回調(diào)執(zhí)行或者輸入不合法的promise就會(huì)立即拋出錯(cuò)誤,并且reject的是第一個(gè)拋出的錯(cuò)誤信息。

語法:Promise.all(iterable)

iterable: 一般為包含 n 個(gè) promise 的數(shù)組

總結(jié):返回一個(gè)新的 promise ,只有所有的 promise 都成功才成功,只要有一個(gè)失敗了就直接失敗

let p1 = new Promise((resolve, reject) => {
  resolve('p1')
})

let p2 = new Promise((resolve, reject) => {
  resolve('p2')
})

let p3 = new Promise((resolve, reject) => {
  resolve('p3')
})

const result = Promise.all([p1, p2, p3])

console.log(result) // 返回值為三個(gè)promise對(duì)象成功的結(jié)果組成的數(shù)組
// 如果有一個(gè)失敗,返回值為失敗,失敗的結(jié)構(gòu)值就為第一個(gè)失敗的那個(gè)結(jié)果值

Promise.race()

這個(gè)方法也是屬于 Promise 函數(shù)對(duì)象的,不屬于實(shí)例對(duì)象。

語法:Promise.race(iterable);

iterable: 包含 n 個(gè) promise 的數(shù)組

總結(jié):返回一個(gè)新的 promise,第一個(gè)完成的promise的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)

就相當(dāng)于在傳入的promise對(duì)象在賽跑,誰先改變狀態(tài),誰就決定race方法的返回結(jié)果,無論是成功或者失敗。

const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

const result = Promise.race([p1, p2]).then((value) => {
  console.log(value);
  // 都成功,但是顯然p2更快,所以result的結(jié)果為two
});

Promise中的幾個(gè)關(guān)鍵問題

1. 如何改變promise對(duì)象的狀態(tài)

let p = new Promise((resolve, reject) => {
  // 1.resolve 函數(shù)
  resolve('ok') // pending => resolved

  // 2. reject 函數(shù)
  reject('error') // pending => rejected

  // 3. 拋出錯(cuò)誤
  throw '出問題了'

  // 這三種方法都可以改變promise 的狀態(tài)
})

2. 能否執(zhí)行多個(gè)回調(diào)

promise指定多個(gè)成功或失敗的回調(diào)函數(shù),都會(huì)調(diào)用嗎?

當(dāng)promise改變?yōu)閷?duì)應(yīng)狀態(tài)時(shí)都會(huì)調(diào)用。

let p = new Promise((resolve, reject) => {
  resolve('ok')
})

// 指定回調(diào) - 1
p.then(value => {
  console.log(value)
})

// 指定回調(diào) - 2
p.then(value => {
  alert(value)
})

// 很顯然只要時(shí)promise的狀態(tài)改變了,上面的兩個(gè)回調(diào)函數(shù)都會(huì)執(zhí)行

3. 改變狀態(tài)與執(zhí)行回調(diào)順序問題

改變promise狀態(tài)和執(zhí)行回調(diào)函數(shù)誰先誰后?

const p = new Promise((resolve, reject) => {
  // 1. 當(dāng)執(zhí)行器中是一個(gè)同步任務(wù)的時(shí)候,那么會(huì)先改變狀態(tài)再執(zhí)行下面的then回調(diào)函數(shù)
  resolve('ok')

  // 2. 當(dāng)執(zhí)行器中時(shí)一個(gè)異步任務(wù)的時(shí)候,那么會(huì)先執(zhí)行下面的then回調(diào)函數(shù)再改變狀態(tài)
  setTimeout(() => {
    resolve('ok')
  }, 1000)
})

p.then((value) => {
  console.log(value)
}, (reason) => {
  console.log(reason)
})
  • 對(duì)于執(zhí)行器中時(shí)一個(gè)異步任務(wù)的時(shí)候,我們先執(zhí)行調(diào)用回調(diào)函數(shù),但是要等到異步任務(wù)中resolve的狀態(tài)改變之后,才會(huì)去執(zhí)行回調(diào)函數(shù)里面的代碼,然后對(duì)成功或失敗的結(jié)果做處理

4. 中斷promise鏈的方法

const p = new Promise((resolve, reject) => {
  resolve(123)
})

p.then(value => {
  console.log(111)
  // 中斷promise鏈的唯一方法是返回一個(gè)pending狀態(tài)的promise對(duì)象
  // 因?yàn)槿绻祷氐氖莗ending,那么then方法返回的也是一個(gè)pending狀態(tài)的promise對(duì)象
  // 那么后續(xù)的then方法都不能執(zhí)行,因?yàn)闋顟B(tài)沒有改變
  return new Promise(() => {})
  // 這時(shí)候就只會(huì)打印111
}).then(value => {
  console.log(222)
}).then(value => {
  console.log(333)
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容