ES6的Promise對象的的介紹及用法(解決回調地獄問題)

一、概述

ES6的Promise對象可解決前端開發中常見的回調地獄問題(即大量異步回調函數的嵌套) 爲異步編程提供了一種很好的解決方案

首先 什麼是異步問題

例:
一個異步的讀取文件的方法

const fs=require("fs")
const path=require("path")

function getFileByPath(filePath)
{
    fs.readFile(filePath,"utf-8",(err,dataStr) => {
        if (err)
        {
            throw err;
        }
        return dataStr;
    })
}

var result=getFileByPath(path.join(__dirname,"./files/1.txt"))
console.log(result)

此時 若執行代碼 輸出的是undefined 而並不是從文件中讀取的內容
這是因爲 fs.readFile是個異步的讀文件方法 主程序不會執行它 而是放入隊列中 讓子程序執行
主程序將其放入隊列中之後就不管它了 然後到了方法結尾 發現沒有return 於是 返回了undefined
這樣就拿不到返回的值

可用回調函數解決此問題:
傳入一個函數 在傳入的函數中獲取到異步方法裏的值進行處理

function getFileByPath(filePath,callback)
{
    fs.readFile(filePath,"utf-8",(err,dataStr) => {
        if (err)
        {
            throw err;
        }
        callback(dataStr)
    })
}

getFileByPath(path.join(__dirname,"./files/1.txt"),function(dataStr){
    console.log(dataStr)
})

兩個回調函數:一個處理成功後的回調 一個處理髮生異常時的回調

const fs=require("fs")
const path=require("path")

function getFileByPath(filePath,successCallback,errorCallback)
{
    fs.readFile(filePath,"utf-8",(err,dataStr) => {
        if (err)
        {

            return errorCallback(err);
        }
        successCallback(dataStr)
    })
}

getFileByPath(path.join(__dirname,"./files/1.txt"),function(data){
        console.log(data);
    },
    function(err){
        console.log(err.message);
    }
)

當有個需求:先讀取文件1 再讀取文件2 最後讀取文件3 並按此順序輸出讀取的內容
此時 若三個函數調用的時候直接是同級並排的 由於是異步函數 因此輸出時會不按規定的順序

若要按指定順序輸出 只能一個個的嵌套:

getFileByPath(path.join(__dirname,"./files/1.txt"),function(data){
    console.log(data);

    getFileByPath(path.join(__dirname,"./files/2.txt"),function(data){
        console.log(data);
        
        getFileByPath(path.join(__dirname,"./files/3.txt"),function(data){
            console.log(data);
        })
    })
})

這種一層又一層的嵌套 即稱爲回調地獄
回調地獄即意爲大量使用了回調函數(即 將一個函數作爲參數傳遞給另個函數)並且有許多以 })結尾的符號
這使得代碼看起來非常混亂

Promise解決回調地域問題的方式是將多層嵌套變成串聯式的函數調用


Promise其實是一個構造函數 可以new Promise() 然後得到一個Promise的實例
裏面有兩個重要的方法:resolve()reject() 其中 resolve()是成功後的回調函數 reject()是失敗後的回調函數

Promise構造函數的Prototype屬性上有個.then()方法 因此 只要是Promise函數創建的實例都可以訪問.then()方法


Promise是一個容器 裏面保存着某個未來纔會結束的事件(異步操作)的結果
每當new了一個Promise實例之後 該實例就表示一個具體的異步操作
該異步操作的結果只有兩種狀態:成功 or 失敗

  • Promise執行成功後需要在內部調用成功的回調函數resolve()將結果返回給調用者
  • Promise執行失敗後需要在內部調用失敗的回調函數reject()將結果返回給調用者

由於Promise的實例是異步操作 因此內部拿到操作的結果後 無法使用return將結果返回給調用者 需要藉助回調函數(callback)將成功或失敗的結果返回給調用者
可在new出來的Promise實例上調用.then()方法 預先地爲該Promise異步操作指定成功和失敗後的回調函數(resolve()或是reject())

function test()
{
	var promise=new Promise(function(){
	    // 異步函數
	})
	
	return promise;
}

test().then(function(){

})

Promise的特點:只要被創建立刻執行傳入的方法

每當new了一個Promise實例的時候 就會立即執行該異步操作中的代碼
也就是說 new的時候 不僅僅是得到一個Promise實例 還會立即調用給Promise構造方法傳遞的方法 執行該方法裏面的異步操作代碼

const fs=require("fs")
const path=require("path")

var promise=new Promise(function(){
    fs.readFile(filePath,"utf-8",(err,dataStr)=>{
        if (err)
        {
            throw err;
        }
        console.log(dataStr)
    })
})

二、用法

通過.then()指定成功和失敗的回調函數:

在如下的實例中:
執行getFileByPath()方法 進入方法體裏面 new了一個Promise實例並立即執行傳入的function
接下來 傳入的function還沒執行完 由於是異步的 立刻return 將promise實例返回到外面
然後在外面拿到了 在此時讀文件的方法還未執行完畢 通過.then()方法 指定了成功回調函數和失敗回調函數
然後 當文件讀取完畢 訪問了resolve(若成功)或reject(若失敗) 此時由於是外面的調用先於讀文件的方法
因此必定能訪問到resolve()和reject() 完美解決了異步無法獲取到數據的問題

const fs=require("fs")
const path=require("path")

function getFileByPath(filePath)
{
    // 在傳入Promise的方法的參數列表中定義resolve和reject這兩個形參
    var promise=new Promise(function(resolve,reject){
        fs.readFile(filePath,"utf-8",(err,dataStr)=>{
            if (err)
            {
                return reject(err);
            }
            resolve(dataStr);
        })
    })

    // 將該promise對象返回出去
    return promise;
}

var mypromise=getFileByPath(path.join(__dirname,"./files/1.txt"))
// 通過then()方法預先指定成功和失敗的方法的實參
// 由於是異步操作 因此是.then()先執行 並在.then()的參數裏具體化了resolve和reject 因此必定能調用成功
mypromise.then(function(data){
        console.log(data)
    },
    function(err){
        console.log(err.message)
    }
)

將Promise簡化:

const fs=require("fs")
const path=require("path")

function getFileByPath(filePath)
{
    return new Promise(function(resolve,reject){
        fs.readFile(filePath,"utf-8",(err,dataStr)=>{
            if (err)
            {
                return reject(err);
            }
            resolve(dataStr);
        })
    })
}

getFileByPath(path.join(__dirname,"./files/1.txt")).then(function(data){
        console.log(data)
    },
    function(err){
        console.log(err.message)
    }
)

使用Promise解決回調地獄問題:

在.then()裏可以返回新的Promise實例 然後繼續用.then()進行下一步的操作

const fs=require("fs")
const path=require("path")

function getFileByPath(filePath)
{
    return new Promise(function(resolve,reject){
        fs.readFile(filePath,"utf-8",(err,dataStr)=>{
            if (err)
            {
                return reject(err);
            }
            resolve(dataStr);
        })
    })
}

// 讀取文件1
getFileByPath(path.join(__dirname,"./files/1.txt"))
.then(function(data){
    console.log(data)

    // 讀取文件2
    return getFileByPath(path.join(__dirname,"./files/2.txt"))
})
.then(function(data){
    console.log(data)

    // 讀取文件3
    return getFileByPath(path.join(__dirname,"./files/3.txt"))
})
.then(function(data){
    console.log(data)
})

Promise的異常捕獲

在Promise中 有兩種異常捕獲的方式:
一種是前面的Promise執行失敗了也不影響後續的Promise的正常執行
另一種是前面的Promise執行失敗了 後續的Promise都不執行

1、異常捕獲方式一:前面的Promise執行失敗了也不影響後續的Promise的正常執行

可爲每個Promise單獨地通過.then()指定失敗後的回調函數
若前面的Promise執行失敗 後續的Promise操作不會被終止 仍然執行

在.then()方法中的第二個參數位置傳遞一個回調方法 捕獲異常 進行處理 然後在裏面返回一個Promise對象給下一個.then()

getFileByPath(path.join(__dirname,"./files/a.txt"))
.then(function(data){
    console.log(data)

    // 讀取文件2
    return getFileByPath(path.join(__dirname,"./files/2.txt"))
},function(err){
    console.log(err.message)
    
    // 返回一個新的Promise 不影響後續的操作執行
    return getFileByPath(path.join(__dirname,"./files/2.txt"))
})
.then(function(data){
    console.log(data)

    // 讀取文件3
    return getFileByPath(path.join(__dirname,"./files/3.txt"))
})
.then(function(data){
    console.log(data)
})
2、異常捕獲方式二:一旦出現報錯 立即終止所有Promise的執行

若前面任何一個Promise執行失敗 立即終止所有Promise的執行 並立即進入catch()進行錯誤捕獲

在最後用.catch()捕獲異常

getFileByPath(path.join(__dirname,"./files/1.txt"))
.then(function(data){
    console.log(data)

    // 讀取文件2
    return getFileByPath(path.join(__dirname,"./files/a.txt"))
})
.then(function(data){
    console.log(data)

    // 讀取文件3
    return getFileByPath(path.join(__dirname,"./files/3.txt"))
})
.then(function(data){
    console.log(data)
})
.catch(function(err){
    console.log(err.message)
})

在JQuery的Ajax中使用Promise:

JQuery發送Ajax請求也支持Promise

普通的AJAX:
<script>
	$(function(){
          $.ajax({
               url:"localhost:8080/api/getData",
               type:"get",
               dataType:"json",
               success:function(data)
               {
                   console.log(data);
               },
               error:function(err)
               {
                   console.log(err);
               }
           })
	   })
</script>
使用Promise:

.then()裏分別傳入兩個成功後和失敗後的回調函數

<script>
	$(function(){
        $.ajax({
             url:"localhost:8080/api/getData",
             type:"get",
             dataType:"json"
         })
         .then(function(data){
             console.log(data);
         },
         function(err){
             console.log(err)
         })
	 })
</script>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章