搜尋
首頁web前端js教程在 JavaScript 中建立您自己的 Promise

Create your own Promise in JavaScript

為什麼?

了解 JavaScript Promises 如何在背景非同步執行回呼。

讓我們用 JavaScript 創建自己的 Promise!我們將遵循 Promise/A 規範,該規範概述了 Promise 如何處理非同步操作、解析、拒絕以及確保可預測的連結和錯誤處理。

為了簡單起見,我們將重點放在 Promises/A 規範中 ✅ 標記的關鍵規則。這不是一個完整的實現,而是一個簡化版本。這是我們將要建構的:

1. 術語

1.1 'promise' 是一個帶有 then 方法的物件或函數,其行為符合此規範。

1.2 thenable'是一個定義了then方法的物件或函數。

1.3 'value' 是任何合法的 JavaScript 值(包括未定義的、thenable 或一個promise)。

1.4 「異常」是使用 throw 語句拋出的值。

1.5 'reason' 是一個值,表示承諾被拒絕的原因。

2. 要求

2.1 承諾狀態

承諾必須處於以下三種狀態之一:待定、已履行或已拒絕。

2.1.1。待處理時,承諾:✅

⟶ 可能會轉換為已完成或已拒絕狀態。

2.1.2。兌現後,承諾:✅

⟶不得轉換到任何其他狀態。

⟶ 必須有一個值,且不得更改。

2.1.3。當被拒絕時,一個承諾:✅

⟶不得轉換到任何其他狀態。

⟶必須有一個理由,而這個理由不能改變。

2.2 then方法

promise 必須提供 then 方法來存取其當前或最終的值或原因。

promise 的 then 方法接受兩個參數:

promise.then(onFulfilled, onRejected);

2.2.1。 onFulfilled 和 onRejected 都是可選參數:✅

⟶ 如果 onFulfilled 不是函數,則必須忽略它。

⟶ 如果 onRejected 不是函數,則必須忽略它。

2.2.2。如果 onFulfilled 是函數: ✅

⟶ 它必須在 Promise 完成後調用,並以 Promise 的值作為第一個參數。

⟶ 在承諾完成之前不得呼叫它。

⟶ 不得多次呼叫。

2.2.3。如果 onRejected 是函數,✅

⟶ 它必須在 Promise 被拒絕後調用,並以 Promise 的原因作為第一個參數。

⟶ 在承諾被拒絕之前不得調用它。

⟶ 不得多次呼叫。

2.2.4。在執行上下文堆疊僅包含平台程式碼之前,不得呼叫 onFulfilled 或 onRejected。 ✅

2.2.5。 onFulfilled 和 onRejected 必須作為函數呼叫(即沒有 this 值)。 ✅

2.2.6。 then 可能會針對同一個 Promise 被多次呼叫。 ✅

⟶ 如果/當 Promise 被履行時,所有對應的 onFulfilled 回呼必須按照它們最初呼叫 then 的順序執行。

⟶ 如果/當 Promise 被拒絕時,所有對應的 onRejected 回呼必須按照其原始呼叫 then 的順序執行。

2.2.7。那就必須回傳一個承諾。 ✅

promise.then(onFulfilled, onRejected);

⟶ 如果 onFulfilled 或 onRejected 傳回值 x,則執行 Promise 解析過程 [[Resolve]](promise2, x)。 ❌

⟶ 如果 onFulfilled 或 onRejected 拋出例外 e,則 Promise2 必須以 e 作為原因被拒絕。 ❌

⟶ 如果 onFulfilled 不是函數且 Promise1 已實現,則 Promise2 必須以與 Promise1 相同的值來實現。 ❌

⟶ 如果 onRejected 不是函數且 Promise1 被拒絕,則 Promise2 也必須以與 Promise1 相同的原因被拒絕。 ❌

執行

JavaScript Promise 採用執行器函數作為參數,函數在 Promise 建立時立即呼叫:

promise2 = promise1.then(onFulfilled, onRejected);
new Promise(excecutor);

核心 Promises/A 規範不涉及如何建立、履行或拒絕 Promise。由你決定。但是您為 Promise 建構提供的實作必須與 JavaScript 中的非同步 API 相容。這是我們 Promise 類別的初稿:

const promise = new Promise((resolve, reject) => {
    // Runs some async or sync tasks
});

規則 2.1(Promise 狀態)規定,Promise 必須處於以下三種狀態之一:待處理、已履行或已拒絕。它還解釋了每個狀態中發生的情況。

當履行或拒絕時,承諾不得轉變為任何其他狀態。因此,我們需要在進行任何轉換之前確保 Promise 處於待處理狀態:

class YourPromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
            }
        };

        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);  // The executor function being called immediately
        } catch (error) {
            reject(error);
        }
    }
}

我們已經知道 Promise 的初始狀態是待處理的,並且我們確保它保持這種狀態,直到明確履行或拒絕:

const resolve = value => {
    if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
    }
};

const reject = reason => {
    if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
    }
};

由於執行器函數在 Promise 實例化後立即被調用,因此我們在建構子方法中調用它:

this.state = 'pending';

我們的 YourPromise 類別的初稿已在此處完成。

Promise/A 規範主要著重於定義可互通的 then() 方法。這個方法讓我們可以存取promise的當前或最終值或原因。讓我們深入探討一下。

規則 2.2(Then 方法)規定 Promise 必須有一個 then() 方法,該方法接受兩個參數:

try {
    executor(resolve, reject);
} catch (error) {
    reject(error);
}

onFulfilled 和 onRejected 都必須在 Promise 完成或拒絕後調用,如果它們是函數,則傳遞 Promise 的值或原因作為它們的第一個參數:

class YourPromise {
    constructor(executor) {
        // Implementation
    }

    then(onFulfilled, onRejected) {
        // Implementation
    }
}

此外,在承諾履行或拒絕之前,不得調用它們,也不得調用超過一次。 onFulfilled 和 onRejected 都是可選的,如果它們不是函數,則應忽略它們。

如果你看規則2.2、2.2.6 和2.2.7,你會發現一個Promise 必須有一個then() 方法,then() 方法可以被多次調用,並且它必須返回一個承諾:

promise.then(onFulfilled, onRejected);

為了簡單起見,我們不會處理單獨的類別或函數。我們將傳回一個 Promise 對象,並傳遞一個執行器函數:

promise2 = promise1.then(onFulfilled, onRejected);

在執行器函數中,如果 Promise 被履行,我們會呼叫 onFulfilled 回呼並使用 Promise 的值來解析它。同樣,如果 Promise 被拒絕,我們會呼叫 onRejected 回呼並以 Promise 的原因拒絕它。

下一個問題是,如果 Promise 仍處於待處理狀態,如何處理 onFulfilled 和 onRejected 回呼?我們將它們排隊以便稍後調用,如下所示:

new Promise(excecutor);

我們完成了。這是 Promise 類別的第二稿,包括 then() 方法:

const promise = new Promise((resolve, reject) => {
    // Runs some async or sync tasks
});

這裡,我們引入兩個欄位:onFulfilledCallbacks 和 onRejectedCallbacks 作為保存回調的佇列。當 Promise 未決時,這些佇列會透過 then() 呼叫填入回調,並且當 Promise 被履行或拒絕時呼叫它們。

繼續測試你的 Promise 類別:

class YourPromise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;

        const resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
            }
        };

        const reject = reason => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);  // The executor function being called immediately
        } catch (error) {
            reject(error);
        }
    }
}

它應該輸出:

const resolve = value => {
    if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
    }
};

const reject = reason => {
    if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
    }
};

另一方面,如果您執行以下測試:

this.state = 'pending';

你會得到:

try {
    executor(resolve, reject);
} catch (error) {
    reject(error);
}

代替:

class YourPromise {
    constructor(executor) {
        // Implementation
    }

    then(onFulfilled, onRejected) {
        // Implementation
    }
}

為什麼?問題在於,當呼叫 then() 時 YourPromise 實例已被解析或拒絕時,then() 方法如何處理回呼。具體來說,當 Promise 狀態不是待處理時,then() 方法不會正確地將回呼的執行延遲到下一個微任務佇列。這會導致同步執行。在我們的範例測試中:

⟶ 承諾立即解決,值為「立即解決」。

⟶ 當呼叫promise.then()時,狀態已經完成,所以onFulfilled回呼會直接執行,不會延遲到下一個微任務佇列。

這裡規則 2.2.4 發揮作用。此規則可確保 then() 回呼(onFulfilled 或 onRejected)非同步執行,即使 Promise 已解決或拒絕。這意味著回調不得運行,直到當前執行堆疊完全清除並且只有平台程式碼(如事件循環或微任務佇列)正在運行。

為什麼這條規則很重要?

這條規則是 Promise/A 規範中最重要的規則之一。因為它確保:

⟶ 即使 Promise 立即解決,其 then() 回調也不會執行,直到事件循環的下一個標記。

⟶ 此行為與 JavaScript 中其他非同步 API(例如 setTimeout 或 process.nextTick)的行為一致。

我們怎樣才能做到這一點?

這可以透過巨集任務機制(如setTimeout或setImmediate)或微任務機制(如queueMicrotask或process.nextTick)來實現。因為微任務或巨集任務或類似機制中的回呼將在目前 JavaScript 執行上下文完成後執行。

為了解決上述問題,我們需要確保即使狀態已經完成或拒絕,相應的回調(onFulfilled或onRejected)也使用queueMicrotask非同步執行。這是修正後的實作:

promise.then(onFulfilled, onRejected);

再次執行前面的範例測試程式碼。您應該得到以下輸出:

promise2 = promise1.then(onFulfilled, onRejected);

就是這樣。

現在,您應該清楚地了解 then() 的回調如何延遲並在下一個微任務佇列中執行,從而實現異步行為。牢牢掌握這個概念對於在 JavaScript 中編寫有效的非同步程式碼至關重要。

下一步是什麼?由於本文沒有涵蓋完整的 Promises/A 規範,您可以嘗試實現其餘部分以獲得更深入的理解。

既然你已經讀到這裡了,希望你喜歡閱讀這篇文章!請分享文章。

追蹤我:
LinkedIn、Medium 和 Github

以上是在 JavaScript 中建立您自己的 Promise的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:性能和效率注意事項Python vs. JavaScript:性能和效率注意事項Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript的起源:探索其實施語言JavaScript的起源:探索其實施語言Apr 29, 2025 am 12:51 AM

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

幕後:什麼語言能力JavaScript?幕後:什麼語言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來:趨勢和預測Python和JavaScript的未來:趨勢和預測Apr 27, 2025 am 12:21 AM

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能