1

I have a web app for which I'm implementing an API that can be used by independent js scripts. Using the recommended package:js/js.dart with @JS annotations, the calls from js to Dart all work fine except for async calls to await a Dart function. The await call throws an exception or doesn't wait.

I have written a small sample VScode project that easily reproduces the problem.

The Dart connect async function returns a Future but when the future is completed an exception is thrown. Is this to do with Dart returning a Future but the javascript expecting a Promise?

There are a lot of posts referencing Dart calls into javascript but I have found very little in the reverse direction (js to Dart) using async/await. I expected the promiseToFuture and futureToPromise functions to perhaps throw some light on this problem but there's not much information out there in the js-to-dart context. The Dart documentation on this issue seems to be non-existent.

There is another odd issue that could just be a symptom. Using 'webdev serve' the await call throws am exception but, if it is caught in a try/catch, the awaits actually do wait and return the completion value. Using 'webdev build' the await calls do not wait at all.

If I have missed the relevant documentation I would be very grateful to be pointed in the right direction. Aside from that, I'd like to hear any suggestions for a working solution!

All the Dart code is in main.dart, built and tested on:

Dart SDK 2.14.4 (stable) (Wed Oct 13 11:11:32 2021 +0200) on "windows_x64": VS Code 1.62.3 (user setup) OS: Windows 10 (Windows_NT x64 10.0.19043) Chrome 96.0.4664.45 (Official Build) (64-bit)

@JS()
library testawait;

import 'dart:async';
import 'dart:core';
import 'package:js/js.dart';

/// main.dart
///
/// This web app is an example of a javascript script await-ing a Dart async
/// function. The Dart async function returns a Future but when the future 
/// is completed an exception is thrown. Is this to do with Dart returning
/// a Future but the javascript expecting a Promise?
/// 
/// The script is triggered by a button (Click me) in the web page (index.html).
///
/// When running with WEBDEV SERVE the awaits respect the Future.delays but throw
/// exceptions and the returns go to the catch.
/// 
/// When running with WEBDEV BUILD the awaits do not delay and the returns go to
/// the next statement.

@JS('connect')
external set _connect(void Function(String host) f);

void main() async {
    _connect = allowInterop(connect);
}

///
/// This causes an exception but the await does wait and the 
/// completion is returned to the catch (with webdev serve):
/// 
///     Uncaught TypeError: T.as is not a function
///     at _Future.new.[_setValue] (future_impl.dart:419)
///
Future<dynamic> connect(String host) async {
    print('main: before connect');
    var completer = Completer<dynamic>();

    // This is just to simulate a connect event being processed
    // elsewhere that completes the completer. This future
    // is not used for anything else.
    Future.delayed(Duration(seconds:2), () {
        print('main: connect done after 3s');
        completer.complete('Future: Connect complete');
    });

    return completer.future;
}

And here is the html that includes the js script; click on the 'click me' button to trigger a call to scriptWaits:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="scaffolded-by" content="https://github.com/dart-lang/sdk">
    <title>testawait</title>
    <script defer src="main.dart.js"></script>
<style>
html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  font-family: 'Roboto', sans-serif;
}

#trigger-div {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

#trigger-round {
  padding: 40px;
  border-radius: 50%;
  background-color: darksalmon;
}
</style>
</head>

<body>
    <div id='trigger-div'>
        <div id='trigger-round'>
            <input type="button" value="Click me" id="script_trigger" onclick="scriptWaits()"></input>
        </div>
    </div>
    <p>open developer tools console to see results</p>

    <script>
    async function scriptWaits() {
        var reply = '';

        console.log('Script: before calling connect');
        try {
            reply = await connect('host');
            console.log('Script: after calling connect, reply=' + reply);
        } catch(e) {
            reply = e;
            console.log('Script: catch connect wait, ' + reply);    
        }
    }
    </script>
</body>
</html>

and pubspec.yaml:

name: testawait
description: Testing javascript await a Dart function
version: 1.0.0

environment:
  sdk: '>=2.14.4 <3.0.0'

dependencies:
  http: ^0.13.3

dev_dependencies:
  build_runner: ^2.1.2
  build_web_compilers: ^3.2.1
  js: ^0.6.3
  lints: ^1.0.0
Dharman
  • 30,962
  • 25
  • 85
  • 135
zapappa
  • 11
  • 3

0 Answers0