Skip to content

Have any examples show how to export methods from dart to JavaScript? #56327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
826327700 opened this issue Jul 28, 2024 · 10 comments
Closed

Have any examples show how to export methods from dart to JavaScript? #56327

826327700 opened this issue Jul 28, 2024 · 10 comments
Labels
area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. type-question A question about expected behavior or functionality web-js-interop Issues that impact all js interop

Comments

@826327700
Copy link

if I have a func in dart:

void add(int a,int b){
  return a+b
}

how to export it to javascript?

and,it can working after dart compile wasm?

@dart-github-bot
Copy link
Collaborator

Summary: The user wants to know how to export a Dart function (add) to JavaScript, specifically for use after compiling Dart to WebAssembly (Wasm). They are looking for examples demonstrating this process.

@dart-github-bot dart-github-bot added area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. triage-automation See https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot. type-question A question about expected behavior or functionality labels Jul 28, 2024
@lrhn lrhn added web-js-interop Issues that impact all js interop and removed triage-automation See https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot. labels Jul 29, 2024
@navaronbracke
Copy link

You are probably looking for https://api.dart.dev/stable/3.4.4/dart-js_interop/JSExport-class.html

Although, https://dart.dev/interop/js-interop/usage does not list any examples of the annotation?

@srujzs
Copy link
Contributor

srujzs commented Jul 29, 2024

toJS e.g.

void add(int a, int b) => a + b;

void useAdd() {
  JSFunction f = add.toJS;
}

Then, you can pass f to JS via interop so that it can call it. This will also work when compiling to Wasm. Note that the are restrictions around what types can go in the type signature: https://dart.dev/interop/js-interop/js-types#requirements-on-external-declarations-and-function-tojs. Here, these types are okay (void, int).

@MaryaBelanger I think we were working on a getting started tutorial for the basic conversions a little while back. Is there a tracking bug for that?

You are probably looking for https://api.dart.dev/stable/3.4.4/dart-js_interop/JSExport-class.html
Although, https://dart.dev/interop/js-interop/usage does not list any examples of the annotation?

JSExport is slightly different. JSExport is intended to wrap and export an entire class instance to JS, but uses Function.toJS under the hood to export individual members. This is useful for things like mocking: https://dart.dev/interop/js-interop/mock. Here, we're exporting a single function, so Function.toJS is what we want.

@sigmundch
Copy link
Member

Then, you can pass f to JS via interop so that it can call it. ...

Just to expand this a tiny bit more. I think it helps if you visualize the idea of "exporting" code as really two steps. A conversion step and a sharing step:

  • convertion: this step is about cross-language mechanics. How do we make it possible to call Dart code outside the Dart runtime (where the Dart calling conventions don't exist)? As @srujzs suggested above, we provide simple conversion helpers like .toJS for this. The @JSExport is also a "convertion" step. The result is a value with meaningful semantics in JavaScript.
  • sharing: this step is about the cross-language interface. Once a value is converted, how we make the code available to a JavaScript program? This usually requires some coordination of both Dart and JavaScript libraries at the application level. Usually consists of a convention for how JS expects to receive the value, logic to read it in JS, and logic in the Dart program to provide it. As @srujzs mentioned, the implementation on the Dart side would use the JSinterop APIs (described in the usage guide)

It may be desirable in the future a mechanism that could do both at once (conversion + sharing), but that's not available today.

@MaryaBelanger I think we were working on a getting started tutorial for the basic conversions a little while back. Is there a tracking bug for that?

Maybe as an intermediate step, it may be worth adding a small example or even a tip to the existing usage page that describes how to provide code outwards? In particular, the page may give the impression that we are only are providing a way to consume/import logic, but the reality is that the same feature works in the other direction: An API you consume that expects a parameter allows you to pass values back, so they can be leveraged as a way to share/export logic.

@826327700
Copy link
Author

My English skills are not very good and I cannot get the information I want to know from here(https://dart.dev/interop/js-interop/mock) . this is my test code:

import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'package:web/web.dart' as web;

/// I don't know if it already works,I try use in js with window.printHelloWorld,it is undefined.
@JSExport()
class Export {
  @JSExport('printHelloWorld')
  void printMessage() => print('Hello World!');
}

void test(){
    print('Hello World!');
}

void main() {
  final now = DateTime.now();
  final element = web.document.querySelector('#output') as web.HTMLDivElement;
  element.text = 'The time is ${now.hour}:${now.minute} '
      'and your Dart web app is running!';
  ///This line of code is valid, I can call it via window.test() in js
    web.window.setProperty('test'.toJS, test.toJS);
}

What I am pursuing is to compile it into wasm through dart, and then provide methods to js. Similar to rust/go compiling wasm and provide some function to js.

@826327700
Copy link
Author

I run the fibonacci function in js and the fibonacci function provided by wasm compiled by dart. Their running efficiency is almost the same. Wasm is not ahead of native js. I can't understand it.

js+html code:

<body>

  <div id="output"></div>
  <div style="display: flex;justify-content: center;">
    <button onclick="testInJs()" style="margin-right: 10px;">test fibonacci in js</button>
    <button onclick="testInDart()">test fibonacci in dart</button>
  </div>
</body>

<script>
function testInJs(){
  console.time("testInJs");
  fibonacci(45)
  console.timeEnd("testInJs");
}
function fibonacci(n) {
  if (n <= 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}
async function testInDart(){
  console.time("testInDart");
  window.fibonacciInDart(45);//this function is export from dart wasm
  console.timeEnd("testInDart");
}
</script>

dart code:

import 'dart:isolate';
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'package:web/web.dart' as web;

@JSExport()
class Export {
  @JSExport('printHelloWorld')
  void printMessage() => print('Hello World!');
}

void test(){
    print('Hello World!');
}

int fibonacci(int n) {
  if (n <= 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

void main() {
  final now = DateTime.now();
  final element = web.document.querySelector('#output') as web.HTMLDivElement;
  element.text = 'The time is ${now.hour}:${now.minute} '
      'and your Dart web app is running!';

    web.window.setProperty('test'.toJS, test.toJS);
    web.window.setProperty('fibonacciInDart'.toJS, fibonacci.toJS);
}

result:
image

@mkustermann
Copy link
Member

mkustermann commented Jul 30, 2024

I run the fibonacci function in js and the fibonacci function provided by wasm compiled by dart. Their running efficiency is almost the same. Wasm is not ahead of native js. I can't understand it.

@826327700 This may be due to having Chrome's DevTools open. If I make this experiment and print timings to the DOM instead (no DevTools open), I get

JS: Took 18986.709999993443 ms, result: 1134903170
Dart: Took 5165.814999997616 ms, result: 1134903170

=> Much faster in Wasm compared with JS

Code:

<!DOCTYPE html>
<html>
<head>
  <title>Test test.dart</title>
  <link rel="preload" href="/test.wasm" as="fetch" crossorigin>
</head>
<body>
  <h1>
  <div id="startup"></div>
  </h1>
  <h3>
    <p></p>
    <div id="js_output">JS: Not run yet.</div>
    <div/>
    <div id="dart_output">Dart: Not run yet.</div>
  </h3>
  <script type="module">
    const dartModulePromise = WebAssembly.compileStreaming(fetch('/test.wasm'));
    const imports = {};
    let dart2wasm_runtime = await import('/test.mjs');
    let moduleInstance =
        await dart2wasm_runtime.instantiate(dartModulePromise, imports);

    dart2wasm_runtime.invoke(moduleInstance);
  </script>
  <script>
    function fibonacci(n) {
      if (n <= 1) {
        return n;
      } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
      }
    }
    function testInJs(){
      const start = performance.now();
      const result = fibonacci(45)
      const ms = performance.now() - start;
      document.querySelector("#js_output").innerHTML = "JS: Took " + ms + " ms, result: " + result;
    }
    async function testInDart(){
      const start = performance.now();
      const result = window.fibonacciInDart(45);//this function is export from dart wasm
      const ms = performance.now() - start;
      document.querySelector("#dart_output").innerHTML = "Dart: Took " + ms + " ms, result: " + result;
    }
  </script>
  <div style="display: flex;justify-content: center;">
    <button onclick="testInJs()" style="margin-right: 10px;">test fibonacci in js</button>
    <button onclick="testInDart()">test fibonacci in dart</button>
  </div>
</body>
</html>
import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'package:web/web.dart' as web;

int fibonacci(int n) {
  if (n <= 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

void main() {
  final now = DateTime.now();
  final element = web.document.querySelector('#startup') as web.HTMLDivElement;
  element.text = 'The time is ${now.hour}:${now.minute} '
      'and your Dart web app is running!';

  web.window.setProperty('fibonacciInDart'.toJS, fibonacci.toJS);
}

@826327700
Copy link
Author

document.querySelector("#js_output").innerHTML = "JS: Took " + ms + " ms, result: " + result;

Indeed, it's because the Chrome DevTool was opened。
Also, can you tell me how to use @JSExport to export functions to JavaScript instead of window.setPropelity ('fibonacciInDart '. toJS, fibonacci.toJS);

@mkustermann
Copy link
Member

Also, can you tell me how to use @JSExport to export functions to JavaScript instead of window.setPropelity ('fibonacciInDart '. toJS, fibonacci.toJS);

I think this @JSExport annotation is meant to be used for "bulk exports", i.e. avoiding many individual window.setProperty(<fooX>.toJS) calls - i.e. a convenience to export many methods at the same time.

For example

import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'package:web/web.dart' as web;

class FooApi {
  @JSExport('fibonacci')
  int fibonacci(int n) => _fibonacci(n);

  @JSExport('add')
  int add(int a, int b) => _add(a, b);

  int _fibonacci(int n) {
    if (n <= 1) {
      return n;
    } else {
      return fibonacci(n - 1) + fibonacci(n - 2);
    }
  }

  int _add(int a, int b) {
    return a + b;
  }
}

void main() {
  // One line to export the `FooApi`.
  web.window.setProperty('foo'.toJS, createJSInteropWrapper(FooApi()));
}

On JS one can then use

window.foo.add(1, 2);
window.foo.fibonacci(42);

or the shorter form

foo.add(1, 2);
foo.fibonacci(42);

since globals are looked up with implicit window.

@826327700
Copy link
Author

window.setProperty('foo'.toJS, createJSInteropWrapper(FooApi()));

it works! thank you very much!

srujzs added a commit to srujzs/site-www that referenced this issue Jul 31, 2024
Addresses dart-lang/sdk#56327 (comment)

Being able to call Dart code from JS is a common
use case (e.g. event listeners). We should add a
section that shows how to export functions and
links to our section on exporting entire Dart
instances.
sfshaza2 pushed a commit to dart-lang/site-www that referenced this issue Aug 1, 2024
Addresses
dart-lang/sdk#56327 (comment)

Being able to call Dart code from JS is a common
use case (e.g. event listeners). We should add a
section that shows how to export functions and
links to our section on exporting entire Dart
instances.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web-js Issues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop. type-question A question about expected behavior or functionality web-js-interop Issues that impact all js interop
Projects
None yet
Development

No branches or pull requests

7 participants