Dart Isolates As Workers - communication

Edited to make the question more clear.
I am trying to work with Isolates (or Web Workers) in Dart. The only ways I can find to communicate between the main and isolate threads are send and call & then from the main thread. But that's a nice way for the main thread to pass some data to the isolate.
What's if I want the isolate to be the one who generates information? Like a game engine that does all the physics in a worker and then sends an updated world information to the main thread? In JavaScript you can send data at any time. Is there an efficient way in Dart? Or do I still have to wait for the main thread to call me and then pass it to it?
P.S. I wonder, does call & then block the thread until reply is done or not?

As of Dart 1.0, you can use isolates like this:
import 'dart:isolate';
import 'dart:async';
void doStuff(SendPort sendPort) {
print('hi from inside isolate');
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((msg) {
print('Received in isolate: [$msg]');
sendPort.send('ECHO: $msg');
});
}
void main() {
SendPort sendPort;
ReceivePort receive = new ReceivePort();
receive.listen((msg) {
if (sendPort == null) {
sendPort = msg;
} else {
print('From isolate: $msg');
}
});
int counter = 0;
Isolate.spawn(doStuff, receive.sendPort).then((isolate) {
new Timer.periodic(const Duration(seconds:1), (t) {
sendPort.send('Count is ${counter++}');
});
});
}

WARNING: this code only works on very old versions of Dart. It does not work on Dart 1.0 or later.
As you mention to post messages to a isolate you need to have a handle on it's sendport.
#import('dart:isolate');
main() {
SendPort sendPort = spawnFunction(doWork);
sendPort.call("hey 1").then((String res) => print("result was: [$res]"));
sendPort.call("hey 2").then((String res) => print("result was: [$res]"));
}
doWork() {
port.receive((msg, reply) {
msg = "msg $msg";
reply.send(msg);
});
}
however since the Dart main thread is itself an isolate you can send data to it by using the global port function:
#import('dart:isolate');
#import('dart:io');
main() {
port.receive((data, reply) {
// in here you can access objects created in the main thread
print("handle [${data['text']}] for index ${data['index']}");
});
SendPort workPort = spawnFunction(doWork);
workPort.send("msg", port.toSendPort());
}
doWork() {
port.receive((msg, reply) {
int i = 0;
new Timer.repeating(1000, (Timer timer) {
i++;
var data = {
"text": "$msg $i",
"index": i
};
print("sending $data");
reply.send(data);
});
});
}
Note there are certain limits about what can be send back and forth between isolates and also currently isolates act differently in JS and on the VM. The current limitations are well described here.

Here is an example where parent creates two isolates and then two isolates also talk to each other along with the parent process.
Parent Code:
import 'dart:isolate';
import 'dart:html';
import 'dart:async';
main() {
querySelector('#output').text = 'Your Dart app is running.';
int counter = 0;
// Parent - Child 1
SendPort csendPort1;
ReceivePort receivePort1 = new ReceivePort();
// Parent - Child 2
SendPort csendPort2;
ReceivePort receivePort2 = new ReceivePort();
// Child1 - Child2
SendPort csendPort11;
SendPort csendPort12;
// Child 1
receivePort1.listen((msg) {
if (csendPort1 == null) {
csendPort1 = msg;
} else if (csendPort11 == null) {
csendPort11 = msg;
} else {
print('$msg');`enter code here`
}
});
bool child1 = false;
Isolate.spawnUri(Uri.parse('child.dart'), [], receivePort1.sendPort).then((isolate) {
print('Child 1 isolate spawned');
new Timer.periodic(const Duration(milliseconds: 500), (t) {
if (csendPort11 != null && csendPort12 != null && child1 == false) {
child1 = true;
csendPort12.send(csendPort11);
} else {
csendPort1.send('Parent-Child1: ${counter++}');
}
});
});
// Child 2
receivePort2.listen((msg) {
if (csendPort2 == null) {
csendPort2 = msg;
} else if (csendPort12 == null) {
csendPort12 = msg;
} else {
print('$msg');
}
});
bool child2 = false;
Isolate.spawnUri(Uri.parse('child.dart'), [], receivePort2.sendPort).then((isolate) {
print('Child 2 isolate spawned');
new Timer.periodic(const Duration(milliseconds: 500), (t) {
if (csendPort11 != null && csendPort12 != null && child2 == false) {
child2 = true;
csendPort11.send(csendPort12);
} else {
csendPort2.send('Parent-Child2: ${counter++}');
}
});
});
}
Child Code:
import 'dart:isolate';
import 'dart:async';
int pcounter = 0;
int ccounter = 0;
SendPort csendPort;
void handleTimeout() {
csendPort.send("${ccounter++}");
}
main(List<String> args, SendPort psendPort) {
// Parent Comm
ReceivePort creceivePort1 = new ReceivePort();
psendPort.send(creceivePort1.sendPort);
creceivePort1.listen((msg) {
psendPort.send('Child-Parent: ${pcounter++} - ${msg}');
});
// Child-Child Comm
ReceivePort creceivePort2 = new ReceivePort();
psendPort.send(creceivePort2.sendPort);
creceivePort2.listen((msg) {
if (csendPort == null) {
csendPort = msg;
csendPort.send("${ccounter++}");
} else {
print("Child-Child: $msg");
var duration = const Duration(milliseconds: 2000);
new Timer(duration, handleTimeout);
}
});
}
HTML Code:
<!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/google/stagehand">
<title>WebIsolateTest</title>
<link rel="stylesheet" href="styles.css">
<script defer src="main.dart" type="application/dart"></script>
<script defer src="packages/browser/dart.js"></script>
</head>
<body>
<div id="output"></div>
</body>
</html>

You can now use the MessageBox class to communicate the other way around. This code sends a message from the Isolate code as soon as it receives the Sink end of the MessageBox. Main thread receives the messages sent from the Isolate and prints it on the console of Dartium. Once you receive the Sink you can launch your game logic and send updates using the sink object received.
import 'dart:html';
import 'dart:isolate';
void main() {
IsolateSink isolateSink = streamSpawnFunction(myIsolateEntryPoint);
MessageBox isolateMessageBox = new MessageBox();
isolateSink.add(isolateMessageBox.sink);
isolateMessageBox.stream.listen((String data) {
print(data);
});
}
void myIsolateEntryPoint() {
stream.listen((IsolateSink messageBoxSink) {
messageBoxSink.add("Test");
});
}

Related

Is there a way to have my service worker intercept fetch requests coming from a client-side SvelteKit load function? (+page.ts)

I'm trying to have a service worker intercept fetch requests coming from a client-side SvelteKit load function. The network requests are being made, but the fetch event is not being triggered.
The fetch request from the load function is going to /api/allTeams, which is cached as reported by chrome devtools, but like I said, it's not getting intercepted. All the function does it fetch the data, and return it in a prop.
Also, every couple minutes I run invalidateAll(), to reload the data, and even those requests aren't being picked up by the SW.
Thanks!
--reese
src/service-worker.js:
import { build, version } from '$service-worker';
self.addEventListener('fetch', function (event) {
console.log("fetch")
event.respondWith(
fetch(event.request).catch(function () {
return caches.match(event.request);
}),
);
});
self.addEventListener('install', async function (event) {
event.waitUntil(
caches.open("ccs-" + version).then(function (cache) {
cache.add("/api/allTeams")
cache.addAll(build)
return;
}),
);
});
src/app.html:
<script>
const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
const registration = await navigator.serviceWorker.register("/service-worker.js", {
scope: "*",
});
if (registration.installing) {
console.log("Service worker installing");
} else if (registration.waiting) {
console.log("Service worker installed");
} else if (registration.active) {
console.log("Service worker active");
}
} catch (error) {
console.error(`Registration failed with ${error}`);
}
}
};
registerServiceWorker()
</script>
src/+page.ts:
export async function load(request: Request) {
const searchQuery = new URL(request.url).searchParams.get("q")
const apiUrl = new URL(request.url)
apiUrl.pathname = "/api/allTeams"
const req = await fetch(apiUrl)
const data = await req.json()
return {data, searchQuery};
}

How to websocket in grails5

I want a grails application in which server sends some message at a fixed interval.
I have tried using spring-websocket plugin in grails, server and client are able to connect but that doesn't fullfill my requirement. i.e., I want, server sends some message at a fixed interval.
This is the server-side code :
package test
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.handler.annotation.SendTo
class ExampleController {
def index() { }
// server
#MessageMapping("/hello")
#SendTo("/topic/hello")
protected String hello(String world) {
List<String> list = new ArrayList<>();
BufferedReader file = new BufferedReader(new FileReader("src/main/resources/dummyLog.txt"));
file.eachLine {line ->
list.add(line)
}
int idx = (int)(Math.random() * list.size());
println idx;
return list.get(idx);
}
}
And this is the client-side code :
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
var socket = new SockJS("${createLink(uri: '/stomp')}");
var client = webstomp.over(socket);
client.connect({}, function() {
client.subscribe("/topic/hello", (message) => {
document.getElementById('helloDiv').append(message.body);
});
});
document.getElementById('helloButton').addEventListener('click', () => {
client.send("/app/hello", JSON.stringify("world"));
});
});
</script>
Thanks.

Dart Isolate SendPort and ReceivePort sending 100times vice versa

My main works, but in my foo I would like to send data to main. Here's my code where I want to pass data between foo and main 100 times. How can I achieve that?
import 'dart:isolate';
import 'dart:async';
void foo(SendPort sendPort) async {
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((dataSend){
print('foo received : ${dataSend}');
print('');
});
}
void main() async {
int temp = 0;
ReceivePort receivePort = new ReceivePort();
Isolate.spawn(foo,receivePort.sendPort);
receivePort.listen((dataSend) {
print('I received : ${dataSend}');
dataSend.send(temp+1);
});
}
I am really confused about what you want to achieve but is it something like this?
import 'dart:isolate';
import 'dart:async';
void foo(SendPort sendPort) async {
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((dataSend) {
print('foo received : ${dataSend}');
sendPort.send(receivePort.sendPort);
});
}
void main() async {
int temp = 0;
ReceivePort receivePort = new ReceivePort();
Isolate.spawn(foo, receivePort.sendPort);
receivePort.listen((dataSend) {
print('I received : ${dataSend}');
if (++temp < 100) {
dataSend.send(temp);
} else {
receivePort.close();
}
});
}

is it possible to lazily use JS libs with Dart?

I am using chartjs (with the dart interface https://pub.dartlang.org/packages/chartjs) and trying to make it deferred by injecting a <script src="chartjs.js"></script> into the head section and awaiting it's load event to then use the lib.
I am getting this exception: Cannot read property 'Chart' of undefined.
It does not happen when the script is within the head of the html before dart.
So, is it possible to load a JS lib after Dart loaded?
this is a problem in DDC.
It addeds require.js to the HTML and conflicts with other libs.
https://github.com/dart-lang/sdk/issues/33979
The solution I've found is to manually remove the header section that uses requirejs from the third-party lib you want to use.
For example, take chartjs: https://cdn.jsdelivr.net/npm/chart.js#2.8.0/dist/Chart.js
You remove this two lines:
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) :
typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) :
Then the file can be lazily added to the DOM without conflicts.
This is my code to lazily fetch scripts:
class ClientUtils {
static final _scriptFetched = <String, Future<bool>>{};
static ScriptElement _scr(String url) => new ScriptElement()
..async = true
..type = 'text/javascript'
..src = url;
static Future<bool> fetchScript(String url,
{String contextCheck}) async {
bool shouldCheck = contextCheck?.isNotEmpty == true;
hasContext() => js.context.hasProperty(contextCheck) &&
js.context[contextCheck] != null;
if (shouldCheck && hasContext())
return true;
if (!_scriptFetched.containsKey(url)) {
Completer<bool> c = new Completer<bool>();
if (!shouldCheck) {
ScriptElement s = _scr(url)
..onLoad.forEach((Event e) {
c.complete(true);
});
document.body.children.add(s);
} else {
Timer.periodic(Duration(milliseconds: 300), (t) {
if (hasContext()) {
t.cancel();
}
c.complete(true);
});
document.body.children.add(_scr(url));
}
_scriptFetched[url] = c.future;
}
return _scriptFetched[url];
}
}
found a better way!
lets remove the define variable after dart loads, then any third-party lib works when added async :D
add this to your main():
import 'dart:js';
void main() {
context.callMethod('fixRequireJs');
}
and in your index.html:
<script type="text/javascript">
window.fixRequireJs = function()
{
console.log('define is ', typeof define);
if (typeof define == 'function') {
console.log('removing define...');
delete define;
window.define = null;
}
}
</script>
You can try the deferred as syntax:
import 'package:chartjs/chartjs.dart' deferred as chartjs;
void main() {
chartjs.loadLibrary().then(() { ... });
}

Web Server send data along with file

I'd like to serve an html page as well as some json to the client without a round-trip that can be processed on the client side. Is this possible? What's the best way of doing this? I've considered sending it in the header but it seems to be frowned upon by some. Any examples are greatly appreciated. Psuedo code:
main(){
...
app.addRequestHandler((req) => req.path == '/user', handler);
}
void handler(req, res) {
var file = new File(myHtmlFile);
res.headers.set(...); //add json here?
res.outputstream... //or here?
...
stream.pipe(res.outputStream);
}
In your html file you can put a tag to be replace before sending as response.
For instance, in your html file :
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<input type='hidden' id='datas' value="___JSON_DATAS___"/>
...
</body>
</html>
___JSON_DATAS___ will be replace in your server with something like :
void handler(req, res) {
var file = new File(myHtmlFile);
readStreamAsString(file.openInputStream()).then((fileContent) {
final content = fileContent
.replaceAll("___JSON_DATAS___", htmlEscape(jsonAsString));
res.outputStream.writeAsString(content);
res.outputStream.close();
});
}
String htmlEscape(String text) {
return text.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "&apos;");
}
Future<String> readStreamAsString(InputStream stream) {
final completer = new Completer();
final sb = new StringBuffer();
final sis = new StringInputStream(stream);
sis
..onData = () { sb.add(sis.read()); }
..onClosed = () { completer.complete(sb.toString()); }
..onError = (e) { completer.completeException(e); };
return completer.future;
}
Then, on client side :
import 'dart:html';
import 'dart:json';
main(){
final input = query('#datas') as InputElement;
final datas = JSON.parse(input.value);
//...
}

Resources