JavaScript Promise介紹

JavaScript Promise介紹

前言

眾所周知,在JavaScript的世界中,代碼都是單線程執(zhí)行的。由于這個原因,JavaScript中的耗時操作,如網(wǎng)絡(luò)操作、瀏覽器事件等,都需要異步執(zhí)行。這也導(dǎo)致在JavaScript中異步操作是非常頻繁且常見的。

異步概念

在執(zhí)行某些耗時、不會立即返回結(jié)果的操作時,不會阻塞后面的操作,一旦該耗時操作完成時,立即通知需要調(diào)用其結(jié)果的函數(shù)來做后續(xù)處理。

簡單來理解就是:同步按照寫的代碼順序執(zhí)行,異步不按照代碼順序執(zhí)行

回調(diào)函數(shù)進行異步操作

和同步操作不同,異步操作是不會立即返回結(jié)果的(如發(fā)起網(wǎng)絡(luò)請求,下載文件,操作數(shù)據(jù)庫等)。如果我們后續(xù)的函數(shù)需要之前返回的結(jié)果,又怎樣使之前的異步操作在其完成時通知到后續(xù)函數(shù)來執(zhí)行呢?

通常,我們可以將這個函數(shù)先定義,存儲在內(nèi)存中,將其當(dāng)做參數(shù)傳入之前的異步操作函數(shù)中,等異步操作結(jié)束,就會調(diào)用執(zhí)行這個函數(shù),這個函數(shù)就叫做回調(diào)函數(shù)(callback)。

舉個栗子:

// 下載
function download(callback){
    // 模擬異步操作
    setTimeout(function(){
        // 調(diào)用回調(diào)函數(shù)
        callback('下載完成');
    }, 1000);
}

function callback(value){
    // 下載完成的處理
    console.log(value);
}

download(callback);

// 這段代碼將在1秒后在控制臺打印“下載完成”

但假如callback函數(shù)同樣是個異步函數(shù),且callback里又嵌入了callback呢? 例如需求是等待第一個文件下載完成后,再下載第二個文件,等待第二個文件下載完成后,再下載第三個文件...,這樣的話,上面這種方法就不可取了,因為會產(chǎn)生很多的函數(shù)嵌套,嵌套太深容易引發(fā)回調(diào)地獄

Promise進行異步操作

古人云:“君子一諾千金”,這種“承諾將來會執(zhí)行”的對象,在JavaScript中稱為Promise對象。

我們首先看看如何通過Promise改造上面的回調(diào)函數(shù)異步操作

var download = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        resolve('下載完成');
    }, 1000);
})

download.then(value => {
    console.log(value);
});

// 這段代碼將在1秒后在控制臺打印“下載完成”

可以看到代碼簡潔了一些,當(dāng)然,不只是如此,它還可以連續(xù)調(diào)用,例如我們上面所說的回調(diào)地獄問題,通過Promise可以很簡潔的實現(xiàn)

var download1 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下載完成');
    }, 1000);
});

var download2 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下載完成');
    }, 1000);
});

var download3 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下載完成');
    }, 1000);
});

download1.then(download2).then(download3);

在download1完成之后會調(diào)用download2,download2完成之后會調(diào)用download3,實現(xiàn)任務(wù)串行,為此,對于那些需要連續(xù)執(zhí)行的異步操作,Promise可以是一種很好的解決辦法。

Promise相對于callback,具有更優(yōu)的代碼流,并且具有很好的靈活性。Promise符合自然的事物執(zhí)行順序,即先做異步操作,然后再用.then告知下一步該做什么。而在Callback的用法中,先得知道下一步做什么,然后才能將其作為callback函數(shù)傳入異步操作函數(shù)中。

認(rèn)識Promise

Promise的創(chuàng)建

Promise創(chuàng)建時,會傳給promise一個稱為excutor執(zhí)行器的函數(shù)。這個excutor我們可以理解為生產(chǎn)者的生產(chǎn)過程函數(shù)。這個函數(shù)含有兩個參數(shù)resolvereject,這倆參數(shù)也同樣是函數(shù),用來傳遞異步操作的結(jié)果

let promise = new Promise(function(resolve, reject) {
  // executor 
})

有幾點值得說一下:

  1. 在Promise對象創(chuàng)建時,excutor會立即執(zhí)行。
  2. resolvereject是JS引擎自動創(chuàng)建的函數(shù),我們無需自己創(chuàng)建,只需將其作為參數(shù)傳入就好。

Promise的狀態(tài)與執(zhí)行流程

Promise執(zhí)行流程圖如下:

image-20210719192052771.png

創(chuàng)建的promise的內(nèi)部狀態(tài)是個對象,初始時為:

{
   state,  //pending
   result,  //undefined
}

當(dāng)Promise中的異步任務(wù)執(zhí)行完,要么產(chǎn)生value,要么產(chǎn)生error,此時會立即調(diào)用resolve(當(dāng)產(chǎn)生value時)或者調(diào)用reject(當(dāng)產(chǎn)生error)時,內(nèi)部狀態(tài)也會隨之改變,如下圖所示:

image-20210719191048764.png

注意,當(dāng)excutor里面即使調(diào)用了多個resolvereject,其最終還是只執(zhí)行一個,其他的都被忽略掉。

let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // ignored
  setTimeout(() => resolve("…")); // ignored
});

多任務(wù)并行

除了串行執(zhí)行若干異步任務(wù)外,Promise還可以并行執(zhí)行異步任務(wù)。

如果一個頁面,需要從多個接口下載文件,但假如這些接口之間沒有依從性,此時我們可以讓多個任務(wù)并行以提升效率。

var download1 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下載完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下載完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下載完成');
    }, 1000);
});

Promise.all([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回一個數(shù)組,包含三個異步任務(wù)的執(zhí)行結(jié)果
});

多任務(wù)競爭

任務(wù)之間是競爭關(guān)系,使用Promise也可以很簡單的實現(xiàn)。

如果一個頁面,需要從多個接口下載文件,但假如只要其中一個任務(wù)執(zhí)行成功獲取其結(jié)果即可,其它任務(wù)便可丟棄。

var download1 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下載完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下載完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模擬異步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下載完成');
    }, 1000);
});

Promise.race([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回download1的執(zhí)行結(jié)果
});

由于download1執(zhí)行較快,所以在then中將獲得download1的結(jié)果,但是download2與download3任然會繼續(xù)執(zhí)行,只是執(zhí)行的結(jié)果將會被丟棄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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