欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

徹底搞懂?javascript的Promise

 更新時(shí)間:2022年02月16日 15:13:55   作者:Always--Learning  
這篇文章主要為大家詳細(xì)介紹了javascript的Promise,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

一、為什么要引入Promise?

在介紹本章之前,首先先拋出幾個(gè)問題:

  • Promise解決了什么問題?
  • Promise有哪些具體的使用場景?

Promise解決了什么問題?

1.回調(diào)地獄問題

在沒有Promise之前,前端獲取數(shù)據(jù)往往需要通過回調(diào)函數(shù)層層嵌套的方式來解決異步問題,例如下面這段代碼實(shí)例:

// 回調(diào)地獄實(shí)例
// 奶茶函數(shù)
function getTea(fn) {
  setTimeout(() => {
    fn('獲取到一杯奶茶')
  },2000)
}
// 面包函數(shù)
function getBread(fn) {
  setTimeout(() => {
    fn('獲取到一個(gè)面包')
  },100)
}
// 如果必須按照順序獲取,而不是根據(jù)時(shí)間,要求是先獲取到奶茶后獲取到面包。
getTea(function(data) {
  console.log(data);
  getBread(function(data) {
    console.log(data);
  }) 
})

2.可讀性問題

通過Promise我們可以將上面的代碼重寫為下面的方式,明顯這樣可讀性更高。

// 下面解釋下,如何通過Promise來解決回調(diào)地獄的問題
function getTea() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('獲取到一杯奶茶')
    }, 2000)
  })
}
function getBread() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('獲取到一個(gè)面包')
    }, 500)
  })
}
getTea()
  .then(res => {
    console.log(res);
    return getBread();
  })
  .then(res => {
    console.log(res);
  })

3.信任問題(也叫回調(diào)多次執(zhí)行問題)

傳統(tǒng)的回調(diào)函數(shù)無法保證只被執(zhí)行一次,回調(diào)函數(shù)還要可能被執(zhí)行其他操作,而Promise調(diào)用且僅調(diào)用一次resolve,不會產(chǎn)生回調(diào)多次執(zhí)行的問題,所以Promise很好的解決了第三方庫多次調(diào)用回調(diào)的問題。

Promise有哪些具體的使用場景?

  • 場景1:將圖片的加載寫成一個(gè)Promise,圖片一旦加載完成,Promise的狀態(tài)就會發(fā)生變化。
  • 場景2:當(dāng)下一個(gè)異步請求需要依賴上一個(gè)請求結(jié)果的時(shí)候,可以通過鏈?zhǔn)讲僮鹘鉀Q問題。
  • 場景3:通過all()實(shí)現(xiàn)多個(gè)請求合并在一起,匯總所有的請求結(jié)果,只需設(shè)置一個(gè)loading即可。
  • 場景4:通過race()可以設(shè)置圖片請求超時(shí)。

二、手寫Prromise身上的方法

手寫Promise.all

Promise.all的特點(diǎn)是接收的是一個(gè)可迭代對象,當(dāng)這個(gè)可迭代對象中的所有元素都執(zhí)行成功會返回一個(gè)數(shù)組,一個(gè)出錯(cuò)則立即返回錯(cuò)誤。

function myPromiseAll(iterable) {
  // 首先明確要返回的對象是一個(gè)Promise
  return new Promise((resolve,reject) => {
    // 首先將可迭代對象轉(zhuǎn)換為數(shù)組
    const promises = Array.from(iterable);
    let flag = 0;
    const result = [];
    // 開始遍歷執(zhí)行
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(res => {
        result[i] = res;
        flag++;
        if (flag === promises.length) {
          resolve(result)
        }
      }).catch(err => {
        reject(err)
      })
    }
  })  
}

手寫Promise.race

Promise.race函數(shù)接收的是一個(gè)可迭代對象,相當(dāng)于讓這個(gè)可迭代對象中的所有promise對象進(jìn)行賽跑,只要有一個(gè)promise對象發(fā)生了狀態(tài)變化,那么直接返回這個(gè)promise對象返回的結(jié)果。

// 手寫promise.race
function myPromiseRace(iterator) {
  // 首先返回的是一個(gè)promise對象
  return new Promise((resolve,reject) => {
    for (let item of iterator) {
      Promise.resolve(item).then(res => {
        resolve(item);
      }).catch(err => {
        reject(err);
      })
    }
  })
}
let p1 = new Promise(resolve => {
  setTimeout(resolve, 105, 'p1 done')
})
let p2 = new Promise(resolve => {
  setTimeout(resolve, 100, 'p2 done')
})
myPromiseRace([p1, p2]).then(data => {
  console.log(data); // p2 done
})

手寫Promise.finally

Promise.finally的特點(diǎn)

  • 無論成功還是失敗,都會執(zhí)行這個(gè)方法
  • 返回的是一個(gè)Promise

Promise.finally執(zhí)行的例子

let p = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve(111);
  },2000)
})
p.then(res => {
  console.log(res);  // 111
}).finally(() => {
  console.log('無論如何這里都會被執(zhí)行');  // 無論如何這里都會被執(zhí)行
})

手寫Promise.finally(Promise.finally返回的本質(zhì)上是一個(gè)then方法,需要在then方法中執(zhí)行我們傳入的參數(shù),然后返回形參)

Promise.prototype.finally = function(f) {
  return this.then((value) => {
    return Promise.resolve(f()).then(() => value)
  },(err) => {
    return Promise.resolve(f()).then(() => {
      throw err;
    })
  })
}

Promise.all和Promise.race的區(qū)別

Promise.all()成功和失敗的返回值是不同的,成功的時(shí)候返回的是一個(gè)結(jié)果數(shù)組,而失敗的時(shí)候返回的是最先被reject的值。當(dāng)Promise.all()的結(jié)果是成功的時(shí)候,返回結(jié)果的數(shù)組里邊的數(shù)據(jù)順序和Promise.all()接收到的promise順序是一致的。

promise.race表示多個(gè)Promise賽跑的意思,里面哪個(gè)結(jié)果執(zhí)行的快就返回哪個(gè)結(jié)果,不管結(jié)果本身是成功還是失敗,其他Promise代碼還會執(zhí)行,只是不會返回。

Promise.all和Promise.race的應(yīng)用場景

promise.all()的應(yīng)用場景

多個(gè)異步任務(wù)都得到結(jié)果時(shí),進(jìn)行顯示的場景

比如,當(dāng)用戶點(diǎn)擊按鈕時(shí),會彈出一個(gè)對話框,這個(gè)對話框中的數(shù)據(jù)來自兩個(gè)不同的后端接口獲取的數(shù)據(jù),當(dāng)用戶剛點(diǎn)擊的時(shí)候,顯示的時(shí)數(shù)據(jù)加載中的狀態(tài),當(dāng)這兩部分?jǐn)?shù)據(jù)都從接口獲取到數(shù)據(jù)的時(shí)候,才讓數(shù)據(jù)加載中的狀態(tài)消失,此時(shí)就可以使用Promise.all方法。

Promise.race()的應(yīng)用場景

提示用戶請求超時(shí)

比如,當(dāng)用戶點(diǎn)擊按鈕發(fā)送請求的時(shí)候,當(dāng)后端的接口超過我們設(shè)定的時(shí)間還沒有獲取到數(shù)據(jù)的時(shí)候,我們就可以提示用戶請求超時(shí)。

三、Promise是如何解決串行和并行的?

什么是并行?什么是串行?

并行:指的是多個(gè)異步請求同時(shí)進(jìn)行。

串行:一個(gè)異步請求完成之后再進(jìn)行下一個(gè)請求。

Promise實(shí)現(xiàn)并行請求

Promise實(shí)現(xiàn)并行請求主要是依靠Promise.all方法和Promise.race方法,我們可以通過手寫Promise.all方法或Promise.race方法來實(shí)現(xiàn)這一目標(biāo)。

Promise實(shí)現(xiàn)串行請求

Promise實(shí)現(xiàn)串行請求主要是借助reduce函數(shù)??梢詤⒖嘉业倪@篇文章如何控制Promise的串行執(zhí)行?

// 借助reduce函數(shù)來實(shí)現(xiàn)Promise的串行執(zhí)行
const funcArr = [
  () => {
    return new Promise((resolve) => {
      setTimeout(() => {resolve(1)},2000)
    })
  },
  () => {
    return new Promise((resolve) => {
      setTimeout(() => {resolve(2)},1000)
    })
  },
  () => {
    return new Promise((resolve) => {
      setTimeout(() => {resolve(3)},3000)
    })
  },
];
function inOrder(arr) {
  const res = [];
  return new Promise((resolve) => {
    arr.reduce((pre,cur) => {
      return pre.then(cur).then(data => res.push(data))
    },Promise.resolve()).then(data => resolve(res))
  })
}
inOrder(funcArr).then(data => console.log(data))   // [1,2,3]

四、什么是Promise穿透?

所謂的Promise的值穿透指的是.then或者.catch的參數(shù)希望是函數(shù),如果傳入的不是函數(shù),則可能會發(fā)生值穿透。Promise方法通過return傳值,沒有return就只是相互獨(dú)立的任務(wù)而已??纯聪旅孢@個(gè)例子的輸出可能會更好的幫助我們理解什么是值穿透?

Promise.resolve(1)
  .then(function(){return 2})
  .then(Promise.resolve(3))
  .then(console.log)   // 2

之所以發(fā)生了值穿透就是因?yàn)榈诙€(gè)then中傳入的不是一個(gè)函數(shù)的形式。

五、使用Promise封裝Ajax請求

使用Promise封裝Ajax請求的關(guān)鍵步驟,全部在下面的代碼中的注釋里,詳情請看下面的代碼。

// 使用Promise封裝Ajax請求
const res = new Promise((resolve,reject) => {
  // 1. 創(chuàng)建一個(gè)XMLHttpRequest對象
  const xhr = new XMLHttpRequest();
  // 2. 初始化請求方法和URL
  xhr.open('GET','https://api.apiopen.top/getJoke');
  // 3. 發(fā)送請求
  xhr.send();
  // 4. 綁定事件,處理響應(yīng)結(jié)果
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      // 這里4代表的就是說服務(wù)端返回了全部的結(jié)果
      // 如果服務(wù)端返回的狀態(tài)碼是2開頭的,我們就resolve這個(gè)返回的結(jié)果,反之則reject對應(yīng)的狀態(tài)碼
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.response)
      } else {
        reject(xhr.status)
      }
    }
  }
})
res.then(function(value) {
  console.log(value);
},function(err) {
  console.log(err);
})

六、Promise有哪些狀態(tài)?

Promise主要有以下三種狀態(tài):

  • pending狀態(tài)(初始狀態(tài))
  • fulfilled狀態(tài)(已經(jīng)成功的狀態(tài))
  • rejected狀態(tài)(已經(jīng)失敗的狀態(tài))

Promise狀態(tài)的變化過程

1.從pending到fulfilled狀態(tài)的切換

resolve前是pending狀態(tài),resolve之后是fulfilled狀態(tài)

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('resolve前的狀態(tài):', p);
    resolve();
    console.log('resolve之后的狀態(tài)', p);
  })
})

image.png

2.從pending狀態(tài)到rejected狀態(tài)

reject前是pending狀態(tài),reject之后是rejected狀態(tài)。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('reject前的狀態(tài):', p);
    reject();
    console.log('reject之后的狀態(tài)', p);
  })
})

image.png

七、將callback改寫成Promise

1.傳統(tǒng)callback的形式

const fs = require('fs');
fs.readFile('./temp.md',(err,data) => {
  console.log(data.toString());
})

2.將callback改為promise的形式

核心就是通過resolve來獲取callback的數(shù)據(jù)。

const fs = require('fs');
async function myReadFile() {
  let result = await new Promise((resolve,reject) => {
    fs.readFile('./temp.md',(err,data) => {
      resolve(data.toString());
    })
  })
  console.log(result);   // xxxxx
  return result;
}
myReadFile()

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!   

相關(guān)文章

最新評論