[pkg:js] Handle null value in promise rejection
Closes https://github.com/dart-lang/sdk/issues/44602 Creates an exception to signal a `null`/`undefined` value when a converted promise is rejected with `null`/`undefined`. Change-Id: Ic7f14e23c6c1d51d6dbcc5831ffa8491418a4267 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192046 Commit-Queue: Srujan Gaddam <srujzs@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
45c4f589a1
commit
6929718456
@@ -149,6 +149,24 @@ dynamic callConstructor(Object constr, List<Object?>? arguments) {
|
||||
// return _wrapToDart(jsObj);
|
||||
}
|
||||
|
||||
/// Exception for when the promise is rejected with a `null` or `undefined`
|
||||
/// value.
|
||||
///
|
||||
/// This is public to allow users to catch when the promise is rejected with
|
||||
/// `null` or `undefined` versus some other value.
|
||||
class NullRejectionException implements Exception {
|
||||
// Indicates whether the value is `undefined` or `null`.
|
||||
final bool isUndefined;
|
||||
|
||||
NullRejectionException._(this.isUndefined);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var value = this.isUndefined ? 'undefined' : 'null';
|
||||
return 'Promise was rejected with a value of `$value`.';
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a JavaScript Promise to a Dart [Future].
|
||||
///
|
||||
/// ```dart
|
||||
@@ -163,7 +181,16 @@ Future<T> promiseToFuture<T>(Object jsPromise) {
|
||||
final completer = Completer<T>();
|
||||
|
||||
final success = convertDartClosureToJS((r) => completer.complete(r), 1);
|
||||
final error = convertDartClosureToJS((e) => completer.completeError(e), 1);
|
||||
final error = convertDartClosureToJS((e) {
|
||||
// Note that `completeError` expects a non-nullable error regardless of
|
||||
// whether null-safety is enabled, so a `NullRejectionException` is always
|
||||
// provided if the error is `null` or `undefined`.
|
||||
if (e == null) {
|
||||
return completer.completeError(
|
||||
NullRejectionException._(JS('bool', '# === undefined', e)));
|
||||
}
|
||||
return completer.completeError(e);
|
||||
}, 1);
|
||||
|
||||
JS('', '#.then(#, #)', jsPromise, success, error);
|
||||
return completer.future;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
@JS()
|
||||
library promise_reject_null_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' show promiseToFuture, NullRejectionException;
|
||||
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String s);
|
||||
|
||||
@JS('Promise.reject')
|
||||
external dynamic getRejectedPromise(v);
|
||||
|
||||
@JS()
|
||||
external void reject(v);
|
||||
@JS()
|
||||
external dynamic getNewPromise();
|
||||
|
||||
void main() async {
|
||||
eval('''
|
||||
self.getNewPromise = function () {
|
||||
return new Promise(function (_, reject) {
|
||||
self.reject = reject;
|
||||
});
|
||||
};
|
||||
''');
|
||||
|
||||
// Rejected promise with a `null` value should trigger a
|
||||
// `NullRejectionException`.
|
||||
await promiseToFuture(getRejectedPromise(null)).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, false);
|
||||
});
|
||||
|
||||
// Similar to the above, except we reject using JS interop.
|
||||
var future = promiseToFuture(getNewPromise()).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, false);
|
||||
});
|
||||
|
||||
reject(null);
|
||||
|
||||
await future;
|
||||
|
||||
// It's also possible to reject with `undefined`. Make sure that the exception
|
||||
// correctly flags that case.
|
||||
future = promiseToFuture(getNewPromise()).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, true);
|
||||
});
|
||||
|
||||
eval('''
|
||||
self.reject(undefined);
|
||||
''');
|
||||
|
||||
await future;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
@JS()
|
||||
library promise_reject_null_test;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'package:js/js_util.dart' show promiseToFuture, NullRejectionException;
|
||||
|
||||
import 'package:expect/minitest.dart';
|
||||
|
||||
@JS()
|
||||
external void eval(String s);
|
||||
|
||||
@JS('Promise.reject')
|
||||
external dynamic getRejectedPromise(v);
|
||||
|
||||
@JS()
|
||||
external void reject(v);
|
||||
@JS()
|
||||
external dynamic getNewPromise();
|
||||
|
||||
void main() async {
|
||||
eval('''
|
||||
self.getNewPromise = function () {
|
||||
return new Promise(function (_, reject) {
|
||||
self.reject = reject;
|
||||
});
|
||||
};
|
||||
''');
|
||||
|
||||
// Rejected promise with a `null` value should trigger a
|
||||
// `NullRejectionException`.
|
||||
await promiseToFuture(getRejectedPromise(null)).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, false);
|
||||
});
|
||||
|
||||
// Similar to the above, except we reject using JS interop.
|
||||
var future = promiseToFuture(getNewPromise()).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, false);
|
||||
});
|
||||
|
||||
reject(null);
|
||||
|
||||
await future;
|
||||
|
||||
// It's also possible to reject with `undefined`. Make sure that the exception
|
||||
// correctly flags that case.
|
||||
future = promiseToFuture(getNewPromise()).then((_) {
|
||||
fail("Expected promise to reject and not fulfill.");
|
||||
}).catchError((e) {
|
||||
expect(e is NullRejectionException, true);
|
||||
expect(e.isUndefined, true);
|
||||
});
|
||||
|
||||
eval('''
|
||||
self.reject(undefined);
|
||||
''');
|
||||
|
||||
await future;
|
||||
}
|
||||
Reference in New Issue
Block a user