ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript で約束のキャンセルをマスターする

JavaScript で約束のキャンセルをマスターする

WBOY
WBOYオリジナル
2024-09-12 10:32:30797ブラウズ

ロザリオ・デ・キアラ作✏️

JavaScript では、Promises は非同期操作を処理するための強力なツールであり、特に UI 関連のイベントで役立ちます。これらは、すぐには利用できないかもしれないが、将来のある時点で解決される値を表します。

Promise により、開発者は API 呼び出し、ユーザー インタラクション、アニメーションなどのタスクを処理する際に、よりクリーンで管理しやすいコードを作成できるようになります (または許可される必要があります)。 Promise では、.then()、.catch()、.finally() などのメソッドを使用することで、成功とエラーのシナリオをより直感的に処理できるようになり、悪名高い「コールバック地獄」を回避できます。

この記事では、新しい (2024 年 3 月) Promise.withResolvers() メソッドを使用します。このメソッドを使用すると、新しい Promise と 2 つの関数 (1 つは Promise を解決するための関数) の 3 つを含むオブジェクトを返すことで、よりクリーンでシンプルなコードを作成できます。これは最近の更新であるため、この記事の例を実行するには最新の Node ランタイム (v>22) が必要です。

新旧の JavaScript Promise メソッドの比較

次の 2 つの機能的に同等のコード部分では、Promise を解決または拒否するメソッドを割り当てる古いアプローチと新しいアプローチを比較できます。

let resolve, reject;

const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

Math.random() > 0.5 ? resolve("ok") : reject("not ok");

上記のコードでは、Promise の最も伝統的な使用方法がわかります。新しい Promise オブジェクトをインスタンス化し、コンストラクターで、resolve と拒否の 2 つの関数を割り当てる必要があります。これらの関数は、次のときに呼び出されます。必要です。

次のコード スニペットでは、同じコード部分が新しい Promise.withResolvers() メソッドで書き直されており、より単純になっています。

const { promise, resolve, reject } = Promise.withResolvers();

Math.random() > 0.5 ? resolve("ok") : reject("not ok");

ここでは、新しいアプローチがどのように機能するかを確認できます。これは Promise を返します。これに対して .then() メソッドと 2 つの関数、resolve および拒否を呼び出すことができます。

Promise への従来のアプローチでは、作成ロジックとイベント処理ロジックが 1 つの関数内にカプセル化されているため、複数の条件やコードの異なる部分で Promise を解決または拒否する必要がある場合に制限が生じる可能性があります。

対照的に、Promise.withResolvers() は、Promise の作成を解決ロジックから分離することで柔軟性を高め、複雑な条件や複数のイベントの管理に適しています。ただし、単純なユースケースの場合、標準的な Promise パターンに慣れている人にとっては、従来の方法の方がシンプルで馴染みやすいかもしれません。

実際の例: API の呼び出し

今度は、より現実的な例で新しいアプローチをテストできるようになりました。以下のコードでは、API 呼び出しの簡単な例を確認できます。

function fetchData(url) {
    return new Promise((resolve, reject) => {
        fetch(url)
            .then(response => {
                // Check if the response is okay (status 200-299)
                if (response.ok) {
                    return response.json(); // Parse JSON if response is okay
                } else {
                    // Reject the promise if the response is not okay
                    reject(new Error('API Invocation failed'));
                }
            })
            .then(data => {
                // Resolve the promise with the data
                resolve(data);
            })
            .catch(error => {
                // Catch and reject the promise if there is a network error
                reject(error);
            });
    });
}

// Example usage
const apiURL = '<ADD HERE YOU API ENDPOINT>';

fetchData(apiURL)
    .then(data => {
        // Handle the resolved data
        console.log('Data received:', data);
    })
    .catch(error => {
        // Handle any errors that occurred
        console.error('Error occurred:', error);
    });

fetchData 関数は、URL を取得し、フェッチ API を使用して API 呼び出しを処理する Promise を返すように設計されています。応答ステータスが 200 ~ 299 の範囲内にあるかどうかを確認して応答を処理し、成功を示します。

成功した場合、応答は JSON として解析され、結果のデータで Promise が解決されます。応答が成功しなかった場合、Promise は拒否され、適切なエラー メッセージが表示されます。さらに、この関数にはネットワーク エラーを検出するためのエラー処理が含まれており、そのようなエラーが発生した場合は Promise を拒否します。

この例では、この関数の使用方法を示し、.then() ブロックで解決されたデータを管理し、.catch() ブロックを使用してエラーを処理し、成功したデータ取得とエラーの両方が適切に管理されるようにする方法を示しています。

以下のコードでは、新しい Promise.withResolvers() メソッドを使用して fetchData() 関数を書き直します。

function fetchData(url) {
    const { promise, resolve, reject } = Promise.withResolvers();

    fetch(url)
        .then(response => {
            // Check if the response is okay (status 200-299)
            if (response.ok) {
                return response.json(); // Parse JSON if response is okay
            } else {
                // Reject the promise if the response is not okay
                reject(new Error('API Invocation failed'));
            }
        })
        .then(data => {
            // Resolve the promise with the data
            resolve(data);
        })
        .catch(error => {
            // Catch and reject the promise if there is a network error
            reject(error);
        });

    return promise;
}

ご覧のとおり、上記のコードはより読みやすく、オブジェクト Promise の役割は明確です。fetchData 関数は、成功するか失敗する Promise を返し、それぞれの場合に適切なメソッドを呼び出します。 。上記のコードは、api.invocation.{old|new}.js.

という名前のリポジトリにあります。

約束のキャンセル

次のコードは、Promise キャンセル メソッドを実装する方法を検討します。ご存知かもしれませんが、JavaScript では Promise をキャンセルできません。 Promise は非同期操作の結果を表し、作成後に解決または拒否するように設計されており、キャンセルする組み込みメカニズムはありません。

この制限は、Promise に定義された状態遷移プロセスがあるために発生します。それらは保留中として開始され、一旦解決されると状態を変更できません。これらは、操作自体を制御するのではなく、操作の結果をカプセル化することを目的としています。つまり、基礎となるプロセスに影響を与えたり、キャンセルしたりすることはできません。この設計の選択により、Promise はシンプルになり、操作の最終的な結果を表すことに重点が置かれます。

const cancellablePromise = () => {
    const { promise, resolve, reject } = Promise.withResolvers();

    promise.cancel = () => {
        reject("the promise got cancelled");
    };
    return promise;
};

In the code above, you can see the object named cancellablePromise, which is a promise with an additional cancel() method that, as you can see, simply forces the invocation of the reject method. This is just syntactic sugar and does not cancel a JavaScript Promise, though it may help in writing clearer code.

An alternative approach is to use an AbortController and AbortSignal, which can be tied to the underlying operation (e.g., an HTTP request) to cancel it when needed. From the documentation, you can see that the AbortController and AbortSignal approach is a more expressive implementation of what we implemented in the code above: once the AbortSignal is invoked, the promise just gets rejected.

Another approach is to use reactive programming libraries like RxJS, which offers an implementation of the Observable pattern, a more sophisticated control over async data streams, including cancellation capabilities.

A comparison between Observables and Promises

When speaking about practical use cases, Promises are well-suited for handling single asynchronous operations, such as fetching data from an API. In contrast, Observables are ideal for managing streams of data, such as user input, WebSocket events, or HTTP responses, where multiple values may be emitted over time.

We already clarified that once initiated, Promises cannot be canceled, whereas Observables allow for cancellation by unsubscribing from the stream. The general idea is that, with Observables, you have an explicit structure of the possible interaction with the object:

  • You create an Observable, and then all the Observables can subscribe to it
  • The Observable carries out its work, changing state and emitting events. All the Observers will receive the updates – this is the main difference with Promises. A Promise can be resolved just once while the Observables can keep emitting events as long as there are Observers
  • Once the Observer is not interested in the events from the Observables, it can unsubscribe, freeing resources

This is demonstrated in the code below:

import { Observable } from 'rxjs';

const observable = new Observable(subscriber => {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});

const observer = observable.subscribe({
  next(x) { console.log('Received value:', x); },
  complete() { console.log('Observable completed'); }
});

observer.unsubscribe();

This code cannot be rewritten with Promises because the Observable returns three values while a Promise can only be resolved once.

To experiment further with the unsubscribe method, we can add another Observer that will use the takeWhile() method: it will let the Observer wait for values to match a specific condition; in the code below, for example, it keeps receiving events from the Observable while the value is not 2:

import { Observable, takeWhile } from 'rxjs';

const observable = new Observable(subscriber => {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});

const observer1 = observable.subscribe({
  next(x) { console.log('Received by 1 value:', x); },
  complete() { console.log('Observable 1 completed'); }
});

const observer2 = observable.pipe(
  takeWhile(value => value != "2")
).subscribe(value => console.log('Received by 2 value:', value));

In the code above, observer1 is the same as we have already seen: it will just subscribe and keep receiving all the events from the Observable. The second one, observer2, will receive elements from the Observable while the condition is matched. In this case, this means when the value is different from 2.

From the execution, you can see how the two different mechanisms work:

$ node observable.mjs
Received by 1 value: 1
Received by 1 value: 2
Received by 1 value: 3
Observable 1 completed
Received by 2 value: 1
$

Conclusion

In this article, we investigated the new mechanism to allocate a Promise in JavaScript and laid out some of the possible ways to cancel a Promise before its completion. We also compared Promises with Observable objects, which not only offer the features of Promises but extend them by allowing multiple emissions of events and a proper mechanism for unsubscribing.


LogRocket: Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.

Mastering promise cancellation in JavaScript

LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Try it for free.

以上がJavaScript で約束のキャンセルをマスターするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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