Use AbortController and AbortSignal to properly cancel fetches.

This commit is contained in:
Steve Browne 2022-07-15 08:50:07 -04:00
parent 5a8818b527
commit 3b409ad3cb
1 changed files with 33 additions and 10 deletions

View File

@ -33,6 +33,19 @@ import 'web_streams.dart';
const _contentTypeKey = 'Content-Type'; const _contentTypeKey = 'Content-Type';
@JS()
class AbortSignal {
external factory AbortSignal();
external bool get aborted;
}
@JS()
class AbortController {
external factory AbortController();
external void abort([dynamic reason]);
external AbortSignal get signal;
}
@anonymous @anonymous
// ignore: missing_js_lib_annotation // ignore: missing_js_lib_annotation
@JS() @JS()
@ -41,6 +54,7 @@ class RequestInit {
{required String method, {required String method,
Object? headers, Object? headers,
List<int>? body, List<int>? body,
AbortSignal? signal,
required String referrerPolicy, required String referrerPolicy,
required String mode, required String mode,
required String credentials, required String credentials,
@ -58,6 +72,9 @@ class RequestInit {
external Uint8List? get body; external Uint8List? get body;
external set body(Uint8List? newValue); external set body(Uint8List? newValue);
external AbortSignal? get signal;
external set signal(AbortSignal? newValue);
external String get referrerPolicy; external String get referrerPolicy;
external set referrerPolicy(String newValue); external set referrerPolicy(String newValue);
@ -106,7 +123,7 @@ class FetchHttpRequest {
Stream<int> get onError => onErrorController.stream; Stream<int> get onError => onErrorController.stream;
// Response information // Response information
CancelableOperation<dynamic>? _cancelableFetch; AbortController? _abortController;
CancelableOperation<dynamic>? _cancelableSend; CancelableOperation<dynamic>? _cancelableSend;
dynamic _response; dynamic _response;
Uint8List? _lastResponse; Uint8List? _lastResponse;
@ -155,6 +172,7 @@ class FetchHttpRequest {
final wgs = WorkerGlobalScope.instance; final wgs = WorkerGlobalScope.instance;
_setReadyState(HttpRequest.LOADING); _setReadyState(HttpRequest.LOADING);
_abortController = AbortController();
final init = RequestInit( final init = RequestInit(
cache: cache, cache: cache,
credentials: credentials, credentials: credentials,
@ -164,17 +182,19 @@ class FetchHttpRequest {
mode: mode, mode: mode,
redirect: redirect, redirect: redirect,
referrerPolicy: referrerPolicy, referrerPolicy: referrerPolicy,
signal: _abortController?.signal,
body: data, body: data,
headers: js_util.jsify(headers)); headers: js_util.jsify(headers));
final operation = _cancelableFetch = CancelableOperation.fromFuture(
js_util.promiseToFuture(js_util.callMethod(wgs, 'fetch', [uri, init])));
_response = await operation.value; _response = await js_util
_setReadyState(HttpRequest.HEADERS_RECEIVED); .promiseToFuture(js_util.callMethod(wgs, 'fetch', [uri, init]))
if (_cancelableSend?.isCanceled ?? false) { .onError((error, stackTrace) => null,
test: (error) => _abortController?.signal.aborted ?? false);
if (_response == null || (_cancelableSend?.isCanceled ?? false)) {
return; return;
} }
_setReadyState(HttpRequest.HEADERS_RECEIVED);
if (status < 200 || status >= 300) { if (status < 200 || status >= 300) {
onErrorController.add(status); onErrorController.add(status);
} }
@ -188,9 +208,11 @@ class FetchHttpRequest {
} }
while (true) { while (true) {
final result = final result = await js_util
await js_util.promiseToFuture(js_util.callMethod(reader, 'read', [])); .promiseToFuture(js_util.callMethod(reader, 'read', []))
if (_cancelableSend?.isCanceled ?? false) { .onError((error, stackTrace) => null,
test: (error) => _abortController?.signal.aborted ?? false);
if (result == null || (_cancelableSend?.isCanceled ?? false)) {
return; return;
} }
final value = js_util.getProperty(result, 'value'); final value = js_util.getProperty(result, 'value');
@ -219,7 +241,7 @@ class FetchHttpRequest {
} }
void abort() async { void abort() async {
await _cancelableFetch?.cancel(); _abortController?.abort();
await _cancelableSend?.cancel(); await _cancelableSend?.cancel();
close(); close();
} }
@ -228,6 +250,7 @@ class FetchHttpRequest {
onReadyStateChangeController.close(); onReadyStateChangeController.close();
onProgressController.close(); onProgressController.close();
onErrorController.close(); onErrorController.close();
_response = null;
} }
void setRequestHeader(String name, String value) { void setRequestHeader(String name, String value) {