異步編程(1):Promise

咱們都知道,ES是單線程語言。因此異步編程對它來講,尤爲重要。也能夠說是他的核心功能。javascript

咱們常見的異步編程有不少,好比 回調函數​、事件監聽 發佈訂閱 Promise等。java

在早期的時候,咱們使用的主要方式是回調函數,可是當咱們有不少回調函數須要依賴的時候,一層套一層,就會造成回調地獄編程

回調地獄既不利於閱讀也不利於維護,因此前輩先賢們爲了解決這個問題,作了不少努力。其中Promise 就是其中的一種方案。promise

Promise

Promise 是一個構造函數,返回一個 promise 對象。該對象有三種狀態,並且狀態不可逆。還有then catch finally 等方法。網上有大量的基礎方法定義,咱們就不在羅列了。異步

使用

then的鏈式調用

咱們知道,Promise支持鏈式調用,那假如中間狀態變成rejected了,會怎麼樣呢。異步編程

let p1 = new Promise((resolve,reject) => {
    setTimeout( () => resolve(200) , 1000)
})

p1.then(() => {
        console.log(11)
    })
    .then(() => {
        throw Error(22)
    })
    .then( () => {
        console.log(33)
    })
    .catch(err => {
        console.log('this is error',err)
    })
// 11
// this is error 22
複製代碼

then 的回調函數返回一個錯誤的時候,返回的是一個rejected`狀態的promise。以後的鏈式調用被終止了,被最後的catch捕獲到。函數

咱們知道then 實際上是能夠接受兩個回調函數的,咱們換一個方式猜下執行順序是什麼呢?post

let p1 = new Promise((resolve,reject) => {
    setTimeout( () => resolve(200) , 1000)
})

p1.then(() => {
        console.log(11)
    })
    .then(() => {
        throw Error(22)
    })
    .then( () => {
        console.log(33)
    },fail => {
        console.log('this is from 3rd fn',fail)
    })
    .catch(err => {
        console.log('this is error',err)
    })
    
// 輸出結果爲:
//11
// this is from 3rd fn

複製代碼

有沒有想到這個結果。ui

從現象看:是由於前面已經處理了rejected 因此後面catch不會在處理了。this

let p1 = new Promise((resolve,reject) => {
    setTimeout( () => resolve(200) , 1000)
})

p1.then(() => {
        console.log(11)
    })
    .then(() => {
        throw Error(22)
    })
    .then( () => {
        console.log(33)
    },fail => {
        console.log('this is from 3rd fn', fail)
    })
    .then( () => {
        console.log(44)
    })
    .catch(err => {
        console.log('this is error',err)
    })
    .then( () => {
        console.log(55)
    })
    
   
// 輸出爲:
// 11
// this is from 3rd fn 22
// 44
// 55
複製代碼

怎麼樣,有沒有想到,處理錯誤以後,還能繼續執行,哪怕在catch後面也能執行。

透過現象看本質,下面咱們對上面幾種狀況用一個統一的解釋來理解下。

  • 首先咱們要知道 then 方法,他接受兩個回調,一個處理 fulilled 狀態, 一個處理 rejected狀態。每次都返回一個新的promise 對象。

  • catch的本質是obj.then(undefined, onRejected)

因此上面的幾種狀況也就好理解了:

then 和 catch 都會返回一個promise 對象。當中間有狀態變爲rejected以後,他會一直往下尋找,找到最近的rejected狀態處理函數,以後根據處理函數的返回值繼續返回不一樣狀態的promise對象。一直執行到鏈式調用的最後。

因此咱們在使用的時候要注意了,避免沒必要要的麻煩。通常用catch 來處理,而且放到最後。這樣能統一處理。

幾個常見題型

let p1 = new Promise((resolve, reject) => {
    console.log(11)
    resolve()
    console.log(22)
  })
  p1.then(() => {
    console.log(33)
  })
  console.log(44)

// 結果爲: 11 、22 、4四、33
複製代碼

解釋:Promise構造函數會當即執行,因此先是十一、resolve、22,因爲then是異步的,因此代碼緊接着是4四、最後纔是33

Promise.resolve(11)
  .then(22)
  .then(Promise.resolve(33))
  .then(console.log)

// 輸出 11
複製代碼

解釋:then 參數不是function的時候都會被忽略。

使用場景

下面咱們來看一下,它的一些經常使用場景:

一、異步b依賴異步a

// 異步請求b 依賴異步請求a
let p1 = new Promise(function(resolve,reject){
  $.post(urla,data,function(res){
    resolve(res);
  })
})

p1.then(function(res){
  $.post(urlb,data,function(){
    // 處理請求返回後的數據
  })
})
複製代碼

二、依賴異步a和異步b

咱們有時候常常會遇到這樣的一種場景,咱們須要發起兩個互不依賴異步請求,而後等待兩個請求都返回以後再處理。

let p1 = new Promise((resolve,reject) =>{
    setTimeout( () => resolve(100), 200)
})

let p2 = new Promise((resolve,reject) =>{
    setTimeout( () => reject(200), 300)
})

Promise.all([p1,p2])
    .then(values =>{
        console.log(values);		// [100,200]
    },fail => {
        console.log(fail)
    })
複製代碼

all方法返回的是一個promise對象,因此他也有 then catch 等方法。

但願上面一點點內容,可以幫助你們加深理解。若有錯誤,不吝指正。

參考連接:

developer.mozilla.org/zh-CN/docs/…

相關文章
相關標籤/搜索