mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:24:42 +00:00
Add unawaited
function and ignore
extensions member.
The `unawaited` function in `dart:async` is intended for use with the `unawaited_futures` lint which is hopefully going to be part of the Dart recommended set of lints. The `ignore` extension method is there to provide an alternative if you even want to ignore errors from a future. By having both, it makes the distinction clearer and makes it easier to not think one can be used for everything. Change-Id: Ib96ed5ff64ead4b228721e5210efa82f76119c9f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200428 Reviewed-by: Jacob Richman <jacobr@google.com> Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
This commit is contained in:
parent
8aec705f69
commit
902f149e2c
|
@ -16,6 +16,11 @@
|
|||
of the zone where they were declared. This prevents a throwing handler
|
||||
from causing an infinite loop by repeatedly triggering itself.
|
||||
|
||||
* Added `ignore()` as extension member on futures.
|
||||
|
||||
* Added `void unawaited(Future)` top-level function to deal with
|
||||
the `unawaited_futures` lint.
|
||||
|
||||
#### `dart:core`
|
||||
|
||||
* The native `DateTime` class now better handles local time around
|
||||
|
|
|
@ -698,6 +698,30 @@ abstract class Future<T> {
|
|||
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
|
||||
}
|
||||
|
||||
/// Explicitly ignores a future.
|
||||
///
|
||||
/// Not all futures need to be awaited.
|
||||
/// The Dart linter has an optional ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html)
|
||||
/// which enforces that futures (expressions with a static type of [Future])
|
||||
/// in asynchronous functions are handled *somehow*.
|
||||
/// If a particular future value doesn't need to be awaited,
|
||||
/// you can call `unawaited(...)` with it, which will avoid the lint,
|
||||
/// simply because the expression no longer has type [Future].
|
||||
/// Using `unawaited` has no other effect.
|
||||
/// You should use `unawaited` to convey the *intention* of
|
||||
/// deliberately not waiting for the future.
|
||||
///
|
||||
/// If the future completes with an error,
|
||||
/// it was likely a mistake to not await it.
|
||||
/// That error will still occur and will be considered unhandled
|
||||
/// unless the same future is awaited (or otherwise handled) elsewhere too.
|
||||
/// Because of that, `unawaited` should only be used for futures that
|
||||
/// are *expected* to complete with a value.
|
||||
/// You can use [FutureExtension.ignore] if you also don't want to know
|
||||
/// about errors from this future.
|
||||
@Since("2.15")
|
||||
void unawaited(Future<void> future) {}
|
||||
|
||||
/// Convenience methods on futures.
|
||||
///
|
||||
/// Adds functionality to futures which makes it easier to
|
||||
|
@ -770,6 +794,32 @@ extension FutureExtensions<T> on Future<T> {
|
|||
handleError(error as E, stackTrace),
|
||||
test: (Object error) => error is E && (test == null || test(error)));
|
||||
}
|
||||
|
||||
/// Completely ignores this future and its result.
|
||||
///
|
||||
/// Not all futures are important, not even if they contain errors,
|
||||
/// for example if a request was made, but the response is no longer needed.
|
||||
/// Simply ignoring a future can result in uncaught asynchronous errors.
|
||||
/// This method instead handles (and ignores) any values or errors
|
||||
/// coming from this future, making it safe to otherwise ignore
|
||||
/// the future.
|
||||
///
|
||||
/// Use `ignore` to signal that the result of the future is
|
||||
/// no longer important to the program, not even if it's an error.
|
||||
/// If you merely want to silence the ["unawaited futures" lint](https://dart-lang.github.io/linter/lints/unawaited_futures.html),
|
||||
/// use the [unawaited] function instead.
|
||||
/// That will ensure that an unexpected error is still reported.
|
||||
@Since("2.15")
|
||||
void ignore() {
|
||||
var self = this;
|
||||
if (self is _Future<T>) {
|
||||
self._ignore();
|
||||
} else {
|
||||
self.then<void>(_ignore, onError: _ignore);
|
||||
}
|
||||
}
|
||||
|
||||
static void _ignore(Object? _, [Object? __]) {}
|
||||
}
|
||||
|
||||
/// Thrown when a scheduled timeout happens while waiting for an async result.
|
||||
|
|
|
@ -69,9 +69,14 @@ class _FutureListener<S, T> {
|
|||
static const int maskTestError = 4;
|
||||
static const int maskWhenComplete = 8;
|
||||
static const int stateChain = 0;
|
||||
// Handles values, passes errors on.
|
||||
static const int stateThen = maskValue;
|
||||
// Handles values and errors.
|
||||
static const int stateThenOnerror = maskValue | maskError;
|
||||
// Handles errors, has errorCallback.
|
||||
static const int stateCatchError = maskError;
|
||||
// Ignores both values and errors. Has no callback or errorCallback.
|
||||
// The [result] future is ignored, its always the same as the source.
|
||||
static const int stateCatchErrorTest = maskError | maskTestError;
|
||||
static const int stateWhenComplete = maskWhenComplete;
|
||||
static const int maskType =
|
||||
|
@ -191,21 +196,40 @@ class _Future<T> implements Future<T> {
|
|||
/// [_FutureListener] listeners.
|
||||
static const int _stateIncomplete = 0;
|
||||
|
||||
/// Flag set when an error need not be handled.
|
||||
///
|
||||
/// Set by the [FutureExtensions.ignore] method to avoid
|
||||
/// having to introduce an unnecessary listener.
|
||||
/// Only relevant until the future is completed.
|
||||
static const int _stateIgnoreError = 1;
|
||||
|
||||
/// Pending completion. Set when completed using [_asyncComplete] or
|
||||
/// [_asyncCompleteError]. It is an error to try to complete it again.
|
||||
/// [_resultOrListeners] holds listeners.
|
||||
static const int _statePendingComplete = 1;
|
||||
static const int _statePendingComplete = 2;
|
||||
|
||||
/// The future has been chained to another future. The result of that
|
||||
/// other future becomes the result of this future as well.
|
||||
/// The future has been chained to another "source" [_Future].
|
||||
///
|
||||
/// The result of that other future becomes the result of this future
|
||||
/// as well, when the other future completes.
|
||||
/// This future cannot be completed again.
|
||||
/// [_resultOrListeners] contains the source future.
|
||||
static const int _stateChained = 2;
|
||||
/// Listeners have been moved to the chained future.
|
||||
static const int _stateChained = 4;
|
||||
|
||||
/// The future has been completed with a value result.
|
||||
static const int _stateValue = 4;
|
||||
///
|
||||
/// [_resultOrListeners] contains the value.
|
||||
static const int _stateValue = 8;
|
||||
|
||||
/// The future has been completed with an error result.
|
||||
static const int _stateError = 8;
|
||||
///
|
||||
/// [_resultOrListeners] contains an [AsyncEror]
|
||||
/// holding the error and stack trace.
|
||||
static const int _stateError = 16;
|
||||
|
||||
/// Mask for the states above except [_stateIgnoreError].
|
||||
static const int _completionStateMask = 30;
|
||||
|
||||
/// Whether the future is complete, and as what.
|
||||
int _state = _stateIncomplete;
|
||||
|
@ -227,8 +251,8 @@ class _Future<T> implements Future<T> {
|
|||
/// and it is not chained to another future.
|
||||
///
|
||||
/// The future is another future that this future is chained to. This future
|
||||
/// is waiting for the other future to complete, and when it does, this future
|
||||
/// will complete with the same result.
|
||||
/// is waiting for the other future to complete, and when it does,
|
||||
/// this future will complete with the same result.
|
||||
/// All listeners are forwarded to the other future.
|
||||
@pragma("vm:entry-point")
|
||||
var _resultOrListeners;
|
||||
|
@ -253,12 +277,14 @@ class _Future<T> implements Future<T> {
|
|||
/// Creates a future that is already completed with the value.
|
||||
_Future.value(T value) : this.zoneValue(value, Zone._current);
|
||||
|
||||
bool get _mayComplete => _state == _stateIncomplete;
|
||||
bool get _isPendingComplete => _state == _statePendingComplete;
|
||||
bool get _mayAddListener => _state <= _statePendingComplete;
|
||||
bool get _isChained => _state == _stateChained;
|
||||
bool get _isComplete => _state >= _stateValue;
|
||||
bool get _hasError => _state == _stateError;
|
||||
bool get _mayComplete => (_state & _completionStateMask) == _stateIncomplete;
|
||||
bool get _isPendingComplete => (_state & _statePendingComplete) != 0;
|
||||
bool get _mayAddListener =>
|
||||
_state <= (_statePendingComplete | _stateIgnoreError);
|
||||
bool get _isChained => (_state & _stateChained) != 0;
|
||||
bool get _isComplete => (_state & (_stateValue | _stateError)) != 0;
|
||||
bool get _hasError => (_state & _stateError) != 0;
|
||||
bool get _ignoreError => (_state & _stateIgnoreError) != 0;
|
||||
|
||||
static List<Function>? _continuationFunctions(_Future<Object> future) {
|
||||
List<Function>? result = null;
|
||||
|
@ -283,7 +309,7 @@ class _Future<T> implements Future<T> {
|
|||
|
||||
void _setChained(_Future source) {
|
||||
assert(_mayAddListener);
|
||||
_state = _stateChained;
|
||||
_state = _stateChained | (_state & _stateIgnoreError);
|
||||
_resultOrListeners = source;
|
||||
}
|
||||
|
||||
|
@ -315,6 +341,10 @@ class _Future<T> implements Future<T> {
|
|||
return result;
|
||||
}
|
||||
|
||||
void _ignore() {
|
||||
_state |= _stateIgnoreError;
|
||||
}
|
||||
|
||||
Future<T> catchError(Function onError, {bool test(Object error)?}) {
|
||||
_Future<T> result = new _Future<T>();
|
||||
if (!identical(result._zone, _rootZone)) {
|
||||
|
@ -337,13 +367,13 @@ class _Future<T> implements Future<T> {
|
|||
Stream<T> asStream() => new Stream<T>.fromFuture(this);
|
||||
|
||||
void _setPendingComplete() {
|
||||
assert(_mayComplete);
|
||||
_state = _statePendingComplete;
|
||||
assert(_mayComplete); // Aka _statIncomplete
|
||||
_state ^= _stateIncomplete ^ _statePendingComplete;
|
||||
}
|
||||
|
||||
void _clearPendingComplete() {
|
||||
assert(_isPendingComplete);
|
||||
_state = _stateIncomplete;
|
||||
_state ^= _statePendingComplete ^ _stateIncomplete;
|
||||
}
|
||||
|
||||
AsyncError get _error {
|
||||
|
@ -365,7 +395,7 @@ class _Future<T> implements Future<T> {
|
|||
|
||||
void _setErrorObject(AsyncError error) {
|
||||
assert(!_isComplete); // But may have a completion pending.
|
||||
_state = _stateError;
|
||||
_state = _stateError | (_state & _stateIgnoreError);
|
||||
_resultOrListeners = error;
|
||||
}
|
||||
|
||||
|
@ -379,7 +409,8 @@ class _Future<T> implements Future<T> {
|
|||
void _cloneResult(_Future source) {
|
||||
assert(!_isComplete);
|
||||
assert(source._isComplete);
|
||||
_state = source._state;
|
||||
_state =
|
||||
(source._state & _completionStateMask) | (_state & _stateIgnoreError);
|
||||
_resultOrListeners = source._resultOrListeners;
|
||||
}
|
||||
|
||||
|
@ -615,7 +646,7 @@ class _Future<T> implements Future<T> {
|
|||
assert(source._isComplete);
|
||||
bool hasError = source._hasError;
|
||||
if (listeners == null) {
|
||||
if (hasError) {
|
||||
if (hasError && !source._ignoreError) {
|
||||
AsyncError asyncError = source._error;
|
||||
source._zone
|
||||
.handleUncaughtError(asyncError.error, asyncError.stackTrace);
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Ensures a context type of [T] for the operand.
|
||||
/// Helper to create [Type] values.
|
||||
Type typeOf<T>() => T;
|
||||
|
||||
/// Ensures a context type of [T] for the operand.
|
||||
void context<T>(T x) {}
|
||||
|
||||
/// Captures the context type of the call and returns the same type.
|
||||
|
@ -34,6 +37,23 @@ extension StaticType<T> on T {
|
|||
T expectStaticType<R extends Exactly<T>>() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Invokes [callback] with the static type of `this`.
|
||||
///
|
||||
/// Allows any operation on the type.
|
||||
T captureStaticType(void Function<X>() callback) {
|
||||
callback<T>();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// Invokes [callback] with the static type of [value].
|
||||
///
|
||||
/// Similar to [StaticType.captureStaticType], but works
|
||||
/// for types like `void` and `dynamic` which do not allow
|
||||
/// extension methods.
|
||||
void captureStaticType<T>(T value, void Function<X>(X value) callback) {
|
||||
callback<T>(value);
|
||||
}
|
||||
|
||||
/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
|
||||
|
|
90
tests/language_2/static_type_helper.dart
Normal file
90
tests/language_2/static_type_helper.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// Helper to create [Type] values.
|
||||
Type typeOf<T>() => T;
|
||||
|
||||
/// Ensures a context type of [T] for the operand.
|
||||
void context<T>(T x) {}
|
||||
|
||||
/// Captures the context type of the call and returns the same type.
|
||||
///
|
||||
/// Can be used to check the context type as:
|
||||
/// ```dart
|
||||
/// int x = contextType(1 /* valid value */)..expectStaticType<Exactly<int>>;
|
||||
/// ```
|
||||
T contextType<T>(Object result) => result as T;
|
||||
|
||||
extension StaticType<T> on T {
|
||||
/// Check the static type.
|
||||
///
|
||||
/// Use as follows (assuming `e` has static type `num`):
|
||||
/// ```dart
|
||||
/// e.expectStaticType<Exactly<num>>() // No context type.
|
||||
/// e.expectStaticType<SubtypeOf<Object>>() // No context type.
|
||||
/// e.expectStaticType<SupertypeOf<int>>() // No context type.
|
||||
/// ```
|
||||
/// or
|
||||
/// ```dart
|
||||
/// e..expectStaticType<Exactly<num>>() // Preserve context type.
|
||||
/// e..expectStaticType<SubtypeOf<Object>>() // Preserve context type.
|
||||
/// e..expectStaticType<SupertypeOf<int>>() // Preserve context type.
|
||||
/// ```
|
||||
/// This will be a *compile-time error* if the static type is not
|
||||
/// as required by the constraints type (the one passed to [Exactly],
|
||||
/// [SubtypeOf] or [SupertypeOf].)
|
||||
T expectStaticType<R extends Exactly<T>>() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Invokes [callback] with the static type of `this`.
|
||||
///
|
||||
/// Allows any operation on the type.
|
||||
T captureStaticType(void Function<X>() callback) {
|
||||
callback<T>();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// Invokes [callback] with the static type of [value].
|
||||
///
|
||||
/// Similar to [StaticType.captureStaticType], but works
|
||||
/// for types like `void` and `dynamic` which do not allow
|
||||
/// extension methods.
|
||||
void captureStaticType<T>(T value, void Function<X>(X value) callback) {
|
||||
callback<T>(value);
|
||||
}
|
||||
|
||||
/// Use with [StaticType.expectStaticType] to expect precisely the type [T].
|
||||
///
|
||||
/// Example use:
|
||||
/// ```dart
|
||||
/// "abc".expectStaticType<Exactly<String>>();
|
||||
/// ```
|
||||
typedef Exactly<T> = T Function(T);
|
||||
|
||||
/// Use with [StaticType.expectStaticType] to expect a subtype of [T].
|
||||
///
|
||||
/// Example use:
|
||||
/// ```dart
|
||||
/// num x = 1;
|
||||
/// x.expectStaticType<SubtypeOf<Object>>();
|
||||
/// ```
|
||||
typedef SubtypeOf<T> = Never Function(T);
|
||||
|
||||
/// Use with [StaticType.expectStaticType] to expect a supertype of [T].
|
||||
///
|
||||
/// Example use:
|
||||
/// ```dart
|
||||
/// num x = 1;
|
||||
/// x.expectStaticType<SupertypeOf<int>>();
|
||||
/// ```
|
||||
typedef SupertypeOf<T> = T Function(Object?);
|
||||
|
||||
/// Checks that an expression is assignable to [T1], [T2] and [Object].
|
||||
///
|
||||
/// This ensures that the static type of the expression is either dynamic,
|
||||
/// Never, or a type assignable to both [T1] and [T2], and if those are
|
||||
/// unrelated, it must be an intersection type.
|
||||
void checkIntersectionType<T1, T2>(T1 v1, T2 v2, Object v3) {}
|
54
tests/lib/async/future_extension_test.dart
Normal file
54
tests/lib/async/future_extension_test.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async' show Completer, runZonedGuarded;
|
||||
import '../../language/static_type_helper.dart';
|
||||
|
||||
void main() {
|
||||
testIgnore();
|
||||
}
|
||||
|
||||
void testIgnore() {
|
||||
var future = Future<int>.value(42);
|
||||
captureStaticType(future.ignore(), <T>(T value) {
|
||||
Expect.equals(typeOf<void>(), T);
|
||||
});
|
||||
|
||||
asyncStart();
|
||||
// Ignored futures can still be listend to.
|
||||
{
|
||||
var c = Completer<int>.sync();
|
||||
var f = c.future;
|
||||
f.ignore();
|
||||
asyncStart();
|
||||
f.catchError((e) {
|
||||
Expect.equals("ERROR1", e);
|
||||
asyncEnd();
|
||||
return 0;
|
||||
});
|
||||
c.completeError("ERROR1");
|
||||
}
|
||||
|
||||
// Ignored futures are not uncaught errors.
|
||||
{
|
||||
asyncStart();
|
||||
bool threw = false;
|
||||
runZonedGuarded(() {
|
||||
var c = Completer<int>.sync();
|
||||
var f = c.future;
|
||||
f.ignore();
|
||||
c.completeError("ERROR2");
|
||||
}, (e, s) {
|
||||
threw = true;
|
||||
Expect.fail("Should not happen: $e");
|
||||
});
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (threw) Expect.fail("Future not ignored.");
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
||||
asyncEnd();
|
||||
}
|
11
tests/lib/async/unawaited_error_test.dart
Normal file
11
tests/lib/async/unawaited_error_test.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
void main() {
|
||||
// The `unawaited` function is not exposed by dart:core.
|
||||
unawaited;
|
||||
// [error line 7, column 3, length 9]
|
||||
// [cfe] Getter not found: 'unawaited'.
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
}
|
52
tests/lib/async/unawaited_test.dart
Normal file
52
tests/lib/async/unawaited_test.dart
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async' show Completer, runZonedGuarded, unawaited;
|
||||
import 'dart:async' as prefix;
|
||||
import '../../language/static_type_helper.dart';
|
||||
|
||||
void main() {
|
||||
testUnawaited();
|
||||
}
|
||||
|
||||
void testUnawaited() {
|
||||
// Exists where expected.
|
||||
prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object?>)>>();
|
||||
|
||||
var future = Future<int>.value(42);
|
||||
captureStaticType(unawaited(future), <T>(value) {
|
||||
Expect.equals(typeOf<void>(), T);
|
||||
});
|
||||
|
||||
asyncStart();
|
||||
// Unawaited futures still throw.
|
||||
{
|
||||
var c = Completer<int>();
|
||||
var f = c.future;
|
||||
unawaited(f);
|
||||
asyncStart();
|
||||
f.catchError((e) {
|
||||
Expect.equals("ERROR1", e);
|
||||
asyncEnd();
|
||||
return 0;
|
||||
});
|
||||
c.completeError("ERROR1");
|
||||
}
|
||||
// Unawaited futures are still uncaught errors.
|
||||
{
|
||||
asyncStart();
|
||||
runZonedGuarded(() {
|
||||
var c = Completer<int>();
|
||||
var f = c.future;
|
||||
unawaited(f);
|
||||
c.completeError("ERROR2");
|
||||
}, (e, s) {
|
||||
Expect.equals("ERROR2", e);
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
||||
asyncEnd();
|
||||
}
|
54
tests/lib_2/async/future_extension_test.dart
Normal file
54
tests/lib_2/async/future_extension_test.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async' show Completer, runZonedGuarded;
|
||||
import '../../language/static_type_helper.dart';
|
||||
|
||||
void main() {
|
||||
testIgnore();
|
||||
}
|
||||
|
||||
void testIgnore() {
|
||||
var future = Future<int>.value(42);
|
||||
captureStaticType(future.ignore(), <T>(T value) {
|
||||
Expect.equals(typeOf<void>(), T);
|
||||
});
|
||||
|
||||
asyncStart();
|
||||
// Ignored futures can still be listend to.
|
||||
{
|
||||
var c = Completer<int>.sync();
|
||||
var f = c.future;
|
||||
f.ignore();
|
||||
asyncStart();
|
||||
f.catchError((e) {
|
||||
Expect.equals("ERROR1", e);
|
||||
asyncEnd();
|
||||
return 0;
|
||||
});
|
||||
c.completeError("ERROR1");
|
||||
}
|
||||
|
||||
// Ignored futures are not uncaught errors.
|
||||
{
|
||||
asyncStart();
|
||||
bool threw = false;
|
||||
runZonedGuarded(() {
|
||||
var c = Completer<int>.sync();
|
||||
var f = c.future;
|
||||
f.ignore();
|
||||
c.completeError("ERROR2");
|
||||
}, (e, s) {
|
||||
threw = true;
|
||||
Expect.fail("Should not happen: $e");
|
||||
});
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (threw) Expect.fail("Future not ignored.");
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
||||
asyncEnd();
|
||||
}
|
11
tests/lib_2/async/unawaited_error_test.dart
Normal file
11
tests/lib_2/async/unawaited_error_test.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
void main() {
|
||||
// The `unawaited` function is not exposed by dart:core.
|
||||
unawaited;
|
||||
// [error line 7, column 3, length 9]
|
||||
// [cfe] Getter not found: 'unawaited'.
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER
|
||||
}
|
54
tests/lib_2/async/unawaited_test.dart
Normal file
54
tests/lib_2/async/unawaited_test.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async' show Completer, runZonedGuarded, unawaited;
|
||||
import 'dart:async' as prefix;
|
||||
import '../../language/static_type_helper.dart';
|
||||
|
||||
void main() {
|
||||
testUnawaited();
|
||||
}
|
||||
|
||||
void testUnawaited() {
|
||||
// Exists where expected.
|
||||
prefix.unawaited.expectStaticType<Exactly<void Function(Future<Object>)>>();
|
||||
|
||||
var future = Future<int>.value(42);
|
||||
captureStaticType(unawaited(future), <T>(value) {
|
||||
Expect.equals(typeOf<void>(), T);
|
||||
});
|
||||
|
||||
asyncStart();
|
||||
// Unawaited futures still throw.
|
||||
{
|
||||
var c = Completer<int>();
|
||||
var f = c.future;
|
||||
unawaited(f);
|
||||
asyncStart();
|
||||
f.catchError((e) {
|
||||
Expect.equals("ERROR1", e);
|
||||
asyncEnd();
|
||||
return 0;
|
||||
});
|
||||
c.completeError("ERROR1");
|
||||
}
|
||||
// Unawaited futures are still uncaught errors.
|
||||
{
|
||||
asyncStart();
|
||||
runZonedGuarded(() {
|
||||
var c = Completer<int>();
|
||||
var f = c.future;
|
||||
unawaited(f);
|
||||
c.completeError("ERROR2");
|
||||
}, (e, s) {
|
||||
Expect.equals("ERROR2", e);
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
||||
asyncEnd();
|
||||
}
|
Loading…
Reference in a new issue