sdk
sdk copied to clipboard
package:js refuses to interloop Future function
It seems like the package:js has problems interlooping simple Future callbacks, such as:
import 'dart:async';
import 'package:js/js.dart';
@JS('myDartFunction')
external set _myDartFunction(Future<List<String>> Function() f);
@JS()
external Future<List<String>> myDartFunction();
void main() {
_myDartFunction = allowInterop(myActualDartFunction);
}
Future<List<String>> myActualDartFunction() async {
Future.delayed(Duration(seconds: 1));
return ['test', 'test'];
}
When calling this function by JS (myDartFunction().then((value) => {console.log(value);})), the following error is thrown:
Uncaught (in promise) TypeError: f is not a function
runUnary zone.dart:1685
handleValue future_impl.dart:147
handleValueCallback future_impl.dart:766
_propagateToListeners future_impl.dart:795
_completeWithValue future_impl.dart:566
load__dart_sdk/async._Future$</</< future_impl.dart:639
_microtaskLoop schedule_microtask.dart:40
_startMicrotaskLoop schedule_microtask.dart:49
_scheduleImmediateWithPromise async_patch.dart:166
promise callback*_scheduleImmediateWithPromise async_patch.dart:164
_scheduleImmediate async_patch.dart:136
_scheduleAsyncCallback schedule_microtask.dart:69
_rootScheduleMicrotask zone.dart:1493
scheduleMicrotask zone.dart:1705
_asyncCompleteWithValue future_impl.dart:638
_asyncComplete future_impl.dart:598
runBody async_patch.dart:108
_async async_patch.dart:123
myActualDartFunction background_executor.dart:15
_checkAndCall operations.dart:334
dcall operations.dart:339
ret dart_sdk.sound.js:57378
<anonymous> debugger eval code:1
zone.dart:1685:53
IIRC, this isn't allowed as Futures aren't Promises. The Future returned by your Dart function in JS won't necessarily have the methods you're expecting, and there isn't a futureToPromise function like there is for promiseToFuture. @sigmundch to confirm, but it seems like it might be useful to add errors here in allowInterop to inform users.
Yes, I believe you are correct @srujzs
I had to do some weird gymnastics to create a JS object with a method that's expected to return a promise: https://github.com/flutter/engine/pull/36253/files#diff-c12aeab766e5e21494fb2a61877c898072f52aa21a8f8663ad9e74144f17357a
futureToPromise would be handy. Or perhaps allowInterop could support async functions (since async is part of the language both for Dart and JS).
@yjbanov I had a similar problem just now. I needed to be able to create a Promise in Dart so that I could pass it through allowInterop() back to a library for which I'm writing some interop for.
I did manage to write a wrapper using the existing @staticInterop annotation.
Perhaps we could include a static interop definition for Promise in the SDK? (a la futureToPromise)
Code sample:
// main.dart This file just wires up a function to the window so that could easily test it.
import 'package:js/js.dart';
import 'package:js/js_util.dart';
import 'dart:html' as html;
@JS()
@staticInterop
class JSWindow {}
extension JSWindowExtension on JSWindow {
external void Function(Promise input) get testFunction;
}
// This one is all I needed, don't forget to call allowInterop() on the executor!
@JS()
@staticInterop
class Promise {
external factory Promise(
void Function(Promise? Function(Object?) resolve, Promise? Function(Object?) reject) executor,
);
}
void main() async {
var jsWindow = html.window as JSWindow;
await Future.delayed(const Duration(seconds: 2));
jsWindow.testFunction(Promise(allowInterop(
(resolve, reject) async {
await Future.delayed(const Duration(seconds: 2));
resolve(
Promise(
allowInterop((resolve2, reject2) {
print('RESOLVING!');
resolve2(42);
}),
),
);
//resolve(42);
//reject(500);
//reject(jsify(<String, dynamic>{
// 'foo': 42,
//}));
},
)));
}
// api.js
window.testFunction = async function(input) {
if(input instanceof Promise) {
input.then((value) => {
console.log(`resolved with: ${value}`);
},
(reason) => {
console.log(`rejected with: ${JSON.stringify(reason)}`);
},
);
}
}
cc @srujzs Another pain point :)
I think the Flutter engine has a similar futureToPromise function to the code above. We do have a JSPromise now in dart:js_interop that users can use as part of JS types, but it's very minimal. We only have a toDart function on it that calls promiseToFuture, but I can totally see a toJS that calls JSPromise's external constructor.