Dart で Thread ライクな処理  for Native & Webdev (1)

ここでは、Dart の 非同期処理について解説します。
また、Native や Flutter だけでなく、Webdevについても解説します。

# Dart の 非同期処理

dart:isolate を利用する事で、Threadライクな非同期処理を実現できます。
ただ、この dart:isolate には制限がって、Webでは動作しません
※ Dart1 系の頃は 動作していました。

ここでは、Dart1系のIsolateに習い、Worker を 利用してThreadライクな非同期処理を実現してみます。


# Dart: isolate

Dartは非同期処理にはFutureを使うと思います。ただ、同時に処理を並行して動作させる事がFutureでは出来ません。

Background で動画のエンコードなどの重い処理を行いながら、Forgroundでは描画のアニメーションを行う。
といった事を行いたい場合、Isolateを利用します。

Isolateは、メモリの共有は出来ませんが、Threadのように動作します。
コードを見るのが早いと思います。

```dart:isolate_example_01.dart

import 'dart:isolate' as iso;

Future main() async {
  iso.ReceivePort receivePort = iso.ReceivePort();
  iso.Isolate.spawn(workerMain, receivePort.sendPort);

  receivePort.listen((message) {
    print("host: ${message}");
  });
}

workerMain(sendPort) async { 
    for(var message in ["01","02","03"]) {
        (sendPort as iso.SendPort).send(message);
        await Future.delayed(Duration(seconds: 1));
    }
}

```

workerMain() を、Isolate 上で起動して、その結果を、
受け取るコードです。


# Worker 

Dart2系では、Webdevでdart:isolate が利用出来ません。
Dart1系のdart:isolate にならい、Worker を利用してみましょう。

```dart:parent.dart
import 'dart:html' as html;

main() async {
  if(html.Worker.supported) {
      var myWorker = new html.Worker("ww.dart.js");
      myWorker.onMessage.listen((event) {
        print("main:receive: ${event.data}");
      });
      myWorker.postMessage("Hello!!");
  } else {
    print('Your browser doesn\'t support web workers.');
  }
}


```

```dart:child.dart

import 'dart:async';
import 'dart:html' as html;

import 'dart:js' as js;
import 'package:js/js.dart' as pjs;
import 'package:js/js_util.dart' as js_util;

@pjs.JS('self')
external dynamic get globalScopeSelf;


Stream<T> callbackToStream<J, T>(String name, T Function(J jsValue) unwrapValue) {
  var controller = StreamController<T>.broadcast(sync: true);
  js_util.setProperty(js.context['self'], name, js.allowInterop((J event) {
    controller.add(unwrapValue(event));
  }));
  return controller.stream;
}

void jsSendMessage( dynamic object, dynamic m) {
  js.context.callMethod('postMessage',[m]);
}

main() {
    callbackToStream('onmessage', (html.MessageEvent e)  {
      return js_util.getProperty(e, 'data');
    }).listen((message) {
      print('>>> ${message}');
      jsSendMessage(js.context, 'callback: ${message}');
    });
}

```


# PS 
## Dart:Isolate 親から子へメッセージを送信したい場合

iso.SendPort を 　メッセージで渡す事ができます。
ので、子側で、(new iso.ReceivePort()).sendPort を、親にメッセージで渡してください!!

```dart:parent.dart
import 'dart:isolate' as iso;
Future<int> main(List<String> args, iso.SendPort sendPoet) async {
  print("Start Main");
  iso.ReceivePort receivePort = iso.ReceivePort();
  receivePort.listen((message) {
    print("main:receive: ${message}");
    if(message is iso.SendPort) {
      (message as iso.SendPort).send("Hello!! Client");
    }
  });
  
  await iso.Isolate.spawnUri(new Uri.file("./child.dart"), [], receivePort.sendPort);

  return 0;
}

```

```dart:child.dart
import 'dart:isolate' as iso;

int main(List<String> args, iso.SendPort sendPort) {
  print("Hello, World!!");
  sendPort.send("Start a child");
  iso.ReceivePort receivePort = iso.ReceivePort();
  receivePort.listen((message) {
    print("child: receive message: ${message}");
    sendPort.send(message);
  });
  sendPort.send(receivePort.sendPort);

  return 0;
}


```

## dart2native の制限

`iso.Isolate.spawnUri` 　を使う場合、dart2native を利用すると、

```
Unsupported operation: Isolate.spawnUri is not supported when using AOT compilation
```

とエラー表示されますので、dart2nativeを利用したい場合は、Isolate.spawnを使用するようにしましょう!!


## webdev の制限

webdev serve だと Worker が上手く動作しませんでした
`dart2js web/xxx.dart -o web/xxx.dart.js` として、Workerは手動でビルドしてください

## code 

今回のコードは以下の通り
https://github.com/kyorohiro/dart.tiny_worker


# 次回

共通のInterface で動作するようにしてみます。