[解決済み] Promise - 約束を強制的にキャンセルすることは可能か?
質問
ES6プロミスを使用して、ネットワークデータの取得を管理していますが、強制的にキャンセルする必要がある場合があります。
基本的にシナリオは、私がUI上でタイプアヘッド検索を行い、リクエストがバックエンドに委ねられ、部分的な入力に基づいて検索を実行するようなものです。このネットワーク リクエスト (#1) には少し時間がかかるかもしれませんが、ユーザーは入力を続け、最終的に別のバックエンド コール (#2) をトリガーします。
ここで#2は当然#1より優先されるので、Promiseのラッピングリクエスト#1をキャンセルしたいと思います。私はすでにデータレイヤーにすべてのPromiseのキャッシュを持っているので、理論的には#2のPromiseを送信しようとしているときにそれを取り出すことができます。
しかし、一旦キャッシュから取り出したら、どのようにプロミス#1をキャンセルするのでしょうか?
どなたかアプローチを提案していただけませんか?
どのように解決するのですか?
モダンなJavaScriptでは - いいえ
プロミスが定着し(hah)、(保留中の)プロミスをキャンセルすることは不可能になったようです。
代わりに、WHATWG (HTML を構築する標準化団体) の一部として、クロスプラットフォーム (Node, Browsers など) のキャンセルプリミティブである
AbortController
. これを使うと
機能
をキャンセルするために使うことができます。
// Take a signal parameter in the function that needs cancellation
async function somethingIWantToCancel({ signal } = {}) {
// either pass it directly to APIs that support it
// (fetch and most Node APIs do)
const response = await fetch('.../', { signal });
// return response.json;
// or if the API does not already support it -
// manually adapt your code to support signals:
const onAbort = (e) => {
// run any code relating to aborting here
};
signal.addEventListener('abort', onAbort, { once: true });
// and be sure to clean it up when the action you are performing
// is finished to avoid a leak
// ... sometime later ...
signal.removeEventListener('abort', onAbort);
}
// Usage
const ac = new AbortController();
setTimeout(() => ac.abort(), 1000); // give it a 1s timeout
try {
await somethingIWantToCancel({ signal: ac.signal });
} catch (e) {
if (e.name === 'AbortError') {
// deal with cancellation in caller, or ignore
} else {
throw e; // don't swallow errors :)
}
}
いいえ、まだできません。
ES6 のプロミスはキャンセルをサポートしていません まだ . その設計は、多くの人が一生懸命に取り組んだものです。 <次ページ 音 キャンセル セマンティクスを正しく理解することは難しく、これは進行中の作業です。GH の "fetch" レポ、esdiscuss、および他のいくつかのレポで興味深い議論が行われていますが、私があなただったら、ただ辛抱強く待つと思います。
でも、でも、でも......キャンセルって本当に大事なんですよ!
そうなんです、現実問題としてキャンセルは 本当に であり、クライアント側プログラミングの重要なシナリオです。Web リクエストを中断するようなケースは重要であり、どこにでもあります。
だから...言語が私をダメにした!
ええ、その点については申し訳ありません。プロミスは他のものが指定される前に最初に入れなければならなかったので、次のような便利なものがないまま入ってしまいました。
.finally
と
.cancel
- は、DOMを経由して仕様に向かうのですが、その途中です。キャンセルは
ではなく
時間的な制約と API デザインへのより反復的なアプローチにすぎません。
では、どうすればいいのでしょうか?
いくつかの選択肢があります。
- のようなサードパーティライブラリを使用する。 ブルーバード のようなサードパーティライブラリを使用することで、仕様よりもずっと速く動くことができ、その結果、キャンセルや他の多くのグッズを持つことができます。
- キャンセルを渡す トークン .
サードパーティライブラリを使うのは、ごく当たり前のことです。トークンについては、このように、メソッドに関数を取り込ませて、それを呼び出すようにすればよいでしょう。
function getWithCancel(url, token) { // the token is for cancellation
var xhr = new XMLHttpRequest;
xhr.open("GET", url);
return new Promise(function(resolve, reject) {
xhr.onload = function() { resolve(xhr.responseText); });
token.cancel = function() { // SPECIFY CANCELLATION
xhr.abort(); // abort request
reject(new Error("Cancelled")); // reject the promise
};
xhr.onerror = reject;
});
};
ということができるようになる。
var token = {};
var promise = getWithCancel("/someUrl", token);
// later we want to abort the promise:
token.cancel();
実際の使用例
last
これはトークンのアプローチではそれほど難しいことではありません。
function last(fn) {
var lastToken = { cancel: function(){} }; // start with no op
return function() {
lastToken.cancel();
var args = Array.prototype.slice.call(arguments);
args.push(lastToken);
return fn.apply(this, args);
};
}
ということができるようになる。
var synced = last(getWithCancel);
synced("/url1?q=a"); // this will get canceled
synced("/url1?q=ab"); // this will get canceled too
synced("/url1?q=abc"); // this will get canceled too
synced("/url1?q=abcd").then(function() {
// only this will run
});
そして、Bacon や Rx のようなライブラリは、observable ライブラリだからといって、ここで "shine" するのではなく、仕様に縛られないことでユーザーレベルのプロミスライブラリが持っているのと同じ利点を持っているだけです。ES2016でobservableがネイティブになるのを待つしかないでしょう。これらは は しかし、typeaheadのためのニフティです。
関連
-
[解決済み] 明示的なプロミス構築のアンチパターンとそれを回避する方法とは?
-
[解決済み] オブジェクトがPromiseであるかどうかを判断するにはどうすればよいですか?
-
[解決済み] std::promiseとは何ですか?
-
[解決済み] JavascriptのPromiseを関数スコープ外で解決する
-
[解決済み] .then()チェーンで以前のプロミス結果にアクセスするにはどうすればよいですか?
-
[解決済み] vanilla ECMAScript 6 の Promise チェーンをキャンセルする
-
[解決済み] 配列からオブジェクトを生成する
-
[解決済み] 文字列のn番目の出現箇所を取得するには?
-
[解決済み] JSHintの'+'前の改行不良の説明
-
[解決済み] jQueryを使用して、すべてのクリックイベントハンドラを削除するにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] vanilla ECMAScript 6 の Promise チェーンをキャンセルする
-
[解決済み] AngularJSのエラーです。Cross Origin リクエストはプロトコルスキーム http, data, chrome-extension, https に対してのみサポートされています。
-
[解決済み] JavaScriptで次の要素/前の要素を取得しますか?
-
[解決済み] 文字列がすべて同じ部分文字列で構成されているかどうかを調べるにはどうすればよいですか?
-
[解決済み] Angularjs - 現在の日付を表示する
-
[解決済み] 文字列のn番目の出現箇所を取得するには?
-
[解決済み] アサインの左側にJavascriptのオブジェクトブラケット表記({ ナビゲーション } =)があります。
-
[解決済み] 兄弟ノードを選択する方法はありますか?
-
[解決済み] JavaScript で css プロパティを使用して HTML 要素の背景色を設定する方法
-
[解決済み] V8 Javascript エンジンのスタンドアロン実行