ホームページ ウェブフロントエンド jsチュートリアル JavaScript で独自の Promise を作成する

JavaScript で独自の Promise を作成する

Dec 28, 2024 pm 01:29 PM

Create your own Promise in JavaScript

なぜ?

JavaScript Promise が内部でどのようにコールバックを非同期的に実行するかを理解するため。

JavaScript で独自の Promise を作成しましょう! Promise/A 仕様に従います。これは、Promise が非同期操作を処理し、解決、拒否し、予測可能なチェーンとエラー処理を保証する方法を概説しています。

話を簡単にするために、Promises/A 仕様の ✅ でマークされている主要なルールに焦点を当てます。これは完全な実装ではなく、簡略化されたバージョンになります。構築するものは次のとおりです:

1. 用語

1.1 'promise' は、動作がこの仕様に準拠する then メソッドを持つオブジェクトまたは関数です。

1.2 thenable' は、then メソッドを定義するオブジェクトまたは関数です。

1.3 「値」は、任意の有効な JavaScript 値 (未定義、thenable、または Promise を含む) です。

1.4 「例外」は、throw ステートメントを使用してスローされる値です。

1.5 'reason' は、Promise が拒否された理由を示す値です。

2. 要件

2.1 プロミスの状態

Promise は、保留中、履行済み、拒否の 3 つの状態のいずれかである必要があります。

2.1.1.保留中の場合、約束: ✅

⟶は、満たされた状態または拒否された状態のいずれかに移行する可能性があります。

2.1.2.果たされたときの約束: ✅

⟶は他の状態に遷移してはなりません。

⟶には値が必要であり、変更することはできません。

2.1.3.拒否された場合の約束: ✅

⟶は他の状態に遷移してはなりません。

⟶には理由があり、それを変えてはなりません。

2.2 当時の方法

Promise は、その現在または最終的な値または理由にアクセスするための then メソッドを提供する必要があります。

Promise の then メソッドは 2 つの引数を受け入れます:

promise.then(onFulfilled, onRejected);
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

2.2.1. onFulfilled と onRejected は両方ともオプションの引数です: ✅

⟶ onFulfilled が関数ではない場合、無視する必要があります。

⟶ onRejected が関数ではない場合、無視する必要があります。

2.2.2. onFulfilled が関数の場合: ✅

⟶ 最初の引数として Promise の値を指定して、Promise が実行された後に呼び出さなければなりません。

⟶ 約束が果たされる前に呼び出してはなりません。

⟶ 複数回呼び出すことはできません。

2.2.3. onRejected が関数の場合、✅

⟶ Promise が拒否された後に、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 を返さなければなりません。 ✅

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 (約束の状態) では、約束は保留、履行、または拒否の 3 つの状態のいずれかでなければならないと規定されています。また、これらの各状態で何が起こるかについても説明します。

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;
    }
};
ログイン後にコピー
ログイン後にコピー

Executor 関数は Promise のインスタンス化直後に呼び出されるため、コンストラクター メソッド内で呼び出します。

this.state = 'pending';
ログイン後にコピー
ログイン後にコピー

YourPromise クラスの最初のドラフトはここで完了します。

Promise/A 仕様は主に、相互運用可能な then() メソッドの定義に焦点を当てています。このメソッドを使用すると、Promise の現在または最終的な値または理由にアクセスできます。それでは、詳しく見ていきましょう。

ルール 2.2 (then メソッド) では、Promise には 2 つの引数を受け入れる then() メソッドが必要であると規定されています。

try {
    executor(resolve, reject);
} catch (error) {
    reject(error);
}
ログイン後にコピー
ログイン後にコピー

onFulfilled と onRejected は両方とも、promise が履行されるか拒否された後に呼び出す必要があります。関数の場合は、promise の値または理由を最初の引数として渡します。

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

    then(onFulfilled, onRejected) {
        // Implementation
    }
}
ログイン後にコピー
ログイン後にコピー

さらに、Promise が履行または拒否される前に呼び出してはならず、複数回呼び出してはなりません。 onFulfilled と onRejected は両方ともオプションであり、関数でない場合は無視する必要があります。

ルール 2.2、2.2.6、および 2.2.7 を見ると、Promise には then() メソッドが必要であり、then() メソッドは複数回呼び出すことができ、メソッドは約束:

promise.then(onFulfilled, onRejected);
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

物事を簡単にするために、個別のクラスや関数は扱いません。 executor 関数を渡して、Promise オブジェクトを返します:

promise2 = promise1.then(onFulfilled, onRejected);
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

executor 関数内で、Promise が履行された場合、onFulfilled コールバックを呼び出し、Promise の値で解決します。同様に、Promise が拒否された場合は、onRejected コールバックを呼び出し、Promise の理由を指定して拒否します。

次の質問は、Promise がまだ保留状態にある場合に、onFulfilled コールバックと onRejected コールバックをどうするかということです。次のように、後で呼び出されるようにそれらをキューに入れます。

new Promise(excecutor);
ログイン後にコピー
ログイン後にコピー

これで完了です。これは、then() メソッドを含む Promise クラスの 2 番目のドラフトです:

const promise = new Promise((resolve, reject) => {
    // Runs some async or sync tasks
});
ログイン後にコピー
ログイン後にコピー

ここでは、コールバックを保持するキューとして、onFulfilledCallbacks と onRejectedCallbacks という 2 つのフィールドを導入します。これらのキューには、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 は、「即時解決」という値で即時に解決されます。

⟶ Promise.then() が呼び出されたとき、状態はすでに満たされているため、onFulfilled コールバックは次のマイクロタスク キューに延期されることなく直接実行されます。

ここで、ルール 2.2.4 が適用されます。このルールにより、Promise がすでに解決または拒否されている場合でも、then() コールバック (onFulfilled または onRejected) が非同期で実行されることが保証されます。これは、現在の実行スタックが完全にクリアされ、プラットフォーム コード (イベント ループやマイクロタスク キューなど) のみが実行されるまで、コールバックを実行してはいけないことを意味します。

このルールがなぜ重要なのでしょうか?

このルールは、Promise/A 仕様の中で最も重要なルールの 1 つです。それは次のことを保証するためです:

⟶ Promise がすぐに解決されたとしても、その then() コールバックはイベント ループの次のティックまで実行されません。

⟶ この動作は、setTimeout や process.nextTick などの JavaScript の他の非同期 API の動作と一致しています。

どうすればこれを達成できるでしょうか?

これは、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 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

javascriptの分解:それが何をするのか、なぜそれが重要なのか javascriptの分解:それが何をするのか、なぜそれが重要なのか Apr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? Apr 04, 2025 pm 05:09 PM

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

JavaScriptは学ぶのが難しいですか? JavaScriptは学ぶのが難しいですか? Apr 03, 2025 am 12:20 AM

JavaScriptを学ぶことは難しくありませんが、挑戦的です。 1)変数、データ型、関数などの基本概念を理解します。2)非同期プログラミングをマスターし、イベントループを通じて実装します。 3)DOM操作を使用し、非同期リクエストを処理することを約束します。 4)一般的な間違いを避け、デバッグテクニックを使用します。 5)パフォーマンスを最適化し、ベストプラクティスに従ってください。

Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は?
または:
Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は? または: Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Apr 04, 2025 pm 05:36 PM

この記事の視差スクロールと要素のアニメーション効果の実現に関する議論では、Shiseidoの公式ウェブサイト(https://www.shisido.co.co.jp/sb/wonderland/)と同様の達成方法について説明します。

JavaScriptの進化:現在の傾向と将来の見通し JavaScriptの進化:現在の傾向と将来の見通し Apr 10, 2025 am 09:33 AM

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Apr 04, 2025 pm 05:12 PM

Console.log出力の違いの根本原因に関する詳細な議論。この記事では、Console.log関数の出力結果の違いをコードの一部で分析し、その背後にある理由を説明します。 �...

See all articles