Promise用法詳解(一)

zero________________發表於2018-08-08

Promise

基本概念
  1. Promise是一個建構函式,所以可以 new 出一個Promise的例項
  2. 在Promise上有兩個函式 resolve(成功之後的回撥函式)和 reject(失敗後的回撥函式)
  3. 在Promise建構函式的prototype屬性上,有一個 .then() 方法。所以只要是Promise建構函式建立的例項,都可以訪問到 .then()方法
  4. Promise表示一個一步操作,每當我們new一個Promise的例項,這個例項就代表具體的非同步操作。
  5. Promise建立的例項,是一個非同步操作,這個非同步操作結果,只有兩種結果
  • 狀態1:非同步執行成功,需要在內部呼叫成功的回撥函式resolve把結果返回給呼叫者
  • 狀態2:非同步執行失敗,需要在內部呼叫失敗的回撥函式reject把結果返回撥用者
  • 由於Promise的例項是一個非同步操作,所以內部拿到操作結果後,無法使用return把操作結果返回給呼叫者,這個時候只能使用回撥函式的形式,把成功或失敗的結果,返回給呼叫者
  1. 我們可以在new出來的Promise例項上,呼叫 .then()方法,預先為這個Promise非同步操作,指定成功(resolve)和失敗(reject)回撥函式
形式上和具體的Promise非同步操作的區別
let parmise = new Promise()

注意:上面new出來promise,只代表形式上的一個非同步操作。就是說,我們只知道他是一個 非同步操作,但做什麼具體非同步事情目前還不清楚。

let promise = new Promise(function() {
    // 這個function內部寫的就是具體的非同步操作
}

上面則是一個具體的非同步操作,其中使用function制定一個具體的非同步操作

Promise的執行時機
  • 每當new一個Promise例項的時候,除了會得到一個promise例項之外,還會立即呼叫我們為Promise建構函式傳遞的那個function,執行function中的非同步程式碼
  • 所以在使用Promise時我們可以用函式進行包裹,使其按需執行

通過 .then()指定回撥函式的時候,成功的回撥函式必須傳,失敗的回撥可以省略

有了前面的這些鋪墊我們來體驗一下Primise的魅力

需求 此時我們有一個簡單的需求,需要去依次去讀取一些檔案的內容。在沒有學習Promise之前我們可能這樣

const fs = require('fs')
// 形參依次代表,讀取路徑,成功回撥,失敗回撥
function getFileByPath(fpath, succCb, errCb) {
    fs.readFile(fpath, 'utf-8', (err, dataStr) => {
        if (err) return errCb(err)
        succCb(dataStr)
    })
}

// 依次讀取
getFileByPath('./1.txt', function (data) {
    console.log(data)
    getFileByPath('./2.txt', function (data) {
        console.log(data)
        getFileByPath('./3.txt', function (data) {
            console.log(data)           
        })
    })
})

這個時候就出現了一個問題: 回撥地獄
這時你想起來Pramise不就是為了來解決回撥地獄的嘛,於是。

const fs = require('fs')
function getFileByPath(fpath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return reject(err)
            resolve(dataStr)
       })
   })
}

// 依次讀取
// 再上一個 .then中,返回一個promise例項,可以繼續使用下一個 .then來處理
getFileByPath('./1.txt')
    .then(function (data) {
     console.log(data)
     return getFileByPath ('./2.txt')
 })
.then(function (data) {
    console.log(data)
    return getFileByPath('./3.txt')
})
    .then(function (data) {
    console.log(data)
})
 

也成功完成需求這裡寫圖片描述

Promise中異常捕獲兩種方式的使用場景

需求 :前面的Promise執行失敗,但是不要影響後續promise正常執行。

  • 此時可以單獨為每個promise通過.then()指定一下失敗的回撥
const fs = require('fs')
function getFileByPath(fpath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return reject(err)
            resolve(dataStr)
       })
   })
}

// 依次讀取
// 注意此處我們寫了一個根本不存在檔案路徑
getFileByPath('./11111111.txt')
    .then(function (data) {
     console.log(data)
     return getFileByPath ('./2.txt')
    }, function (err) {
        console.log('這是失敗的結果:' + err.message)
        // return一個新的 Promise
        return getFileByPath('./2.txt')
 })
.then(function (data) {
    console.log(data)
    return getFileByPath('./3.txt')
})
    .then(function (data) {
    console.log(data)
})

結果如圖這裡寫圖片描述
需求:前面的Promise執行失敗,後面Promise依賴於前面Promise執行結果,如果前面失敗了後面也沒有繼續執行下去的意義了。

  • 此時可以使用 .catch()進行異常捕獲,只要前面Promise有任何一個執行失敗,立即終止所有的Promise的執行,並馬上進入catch中去處理Promise中丟擲的異常。
const fs = require('fs')
function getFileByPath(fpath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(fpath, 'utf-8', (err, dataStr) => {
            if (err) return reject(err)
            resolve(dataStr)
       })
   })
}

// 依次讀取
getFileByPath('./1.txt')
    .then(function (data) {
        console.log(data)
        // 注意此處我們寫了一個根本不存在檔案路徑
     return getFileByPath ('./22222.txt')
    })
    .then(function (data) {
    console.log(data)
    return getFileByPath('./3.txt')
})
   .then(function (data) {
    console.log(data)
})
// catch的作用: 如果前面有任何Promise執行失敗,則立即終止所有Pormise執行,並進入Promise中去處理Promise丟擲的異常     
    .catch(function (err) {
    console.log("catch來捕獲:" + err.message)
})

結果如圖這裡寫圖片描述