1 | | | import 'dart:async'; |
2 | | | import 'dart:js_interop'; |
3 | | |
|
4 | | | import 'package:logger/web.dart'; |
5 | | | import 'package:meta/meta.dart'; |
6 | | | import 'package:web/web.dart' as web; |
7 | | |
|
8 | | | import '../../exceptions/squadron_error.dart'; |
9 | | | import '../../exceptions/squadron_exception.dart'; |
10 | | | import '../../typedefs.dart'; |
11 | | | import '../../worker/worker_channel.dart'; |
12 | | | import '../../worker/worker_response.dart'; |
13 | | | import '../xplat/_transferables.dart'; |
14 | | |
|
15 | | | /// [WorkerChannel] implementation for the JavaScript world. |
16 | | | final class _WebWorkerChannel implements WorkerChannel { |
17 | | | _WebWorkerChannel._(this._sendPort, this._logger); |
18 | | |
|
19 | | | /// [web.MessagePort] to communicate with the [web.Worker] if the channel is |
20 | | | /// owned by the worker owner. Otherwise, [web.MessagePort] to return values |
21 | | | /// to the client. |
22 | | | final web.MessagePort _sendPort; |
23 | | |
|
24 | | | final Logger? _logger; |
25 | | |
|
26 | | 1 | void _postResponse(WorkerResponse res) { |
27 | | 1 | try { |
28 | | 1 | _sendPort.postMessage(res.wrapInPlace().jsify()); |
29 | | 0 | } catch (ex, st) { |
30 | | 1 | _logger?.e(() => 'Failed to post response $res: $ex'); |
31 | | 0 | throw SquadronErrorExt.create('Failed to post response: $ex', st); |
32 | | | } |
33 | | 1 | } |
34 | | |
|
35 | | 0 | void _inspectAndPostResponse(WorkerResponse res) { |
36 | | 0 | try { |
37 | | 0 | final data = res.wrapInPlace(); |
38 | | 0 | final transfer = Transferables.get(data); |
39 | | 0 | if (transfer == null || transfer.isEmpty) { |
40 | | 0 | _sendPort.postMessage(data.jsify()); |
41 | | | } else { |
42 | | 0 | _sendPort.postMessage(data.jsify(), transfer.jsify() as JSArray); |
43 | | | } |
44 | | 0 | } catch (ex, st) { |
45 | | 1 | _logger?.e(() => 'Failed to post response $res: $ex'); |
46 | | 0 | throw SquadronErrorExt.create('Failed to post response: $ex', st); |
47 | | | } |
48 | | 0 | } |
49 | | |
|
50 | | | /// Sends the [web.MessagePort] to communicate with the [web.Worker]. This |
51 | | | /// method must be called by the [web.Worker] upon startup. |
52 | | | @override |
53 | | | void connect(PlatformChannel channelInfo) => inspectAndReply(channelInfo); |
54 | | |
|
55 | | | /// Sends a [WorkerResponse] with the specified data to the worker client. |
56 | | | /// This method must be called from the [web.Worker] only. On Web patforms, |
57 | | | /// this version does not check arguments for transferable objects. |
58 | | | @override |
59 | | 1 | void reply(dynamic data) => _postResponse(WorkerResponse.withResult(data)); |
60 | | |
|
61 | | | /// Sends a [WorkerResponse] with the specified data to the worker client. |
62 | | | /// This method must be called from the [web.Worker] only. On Web patforms, |
63 | | | /// this version (tentatively) checks arguments for transferable objects. |
64 | | | @override |
65 | | 0 | void inspectAndReply(dynamic data) => |
66 | | 0 | _inspectAndPostResponse(WorkerResponse.withResult(data)); |
67 | | |
|
68 | | | @override |
69 | | | void log(LogEvent message) => _postResponse(WorkerResponse.log(message)); |
70 | | |
|
71 | | | /// Checks if [stream] can be streamed back to the worker client. Returns |
72 | | | /// `true` for browser platforms. |
73 | | | @override |
74 | | | bool canStream(Stream<dynamic> stream) => true; |
75 | | |
|
76 | | | /// Sends a [WorkerResponse.closeStream] to the worker client. This method |
77 | | | /// must be called from the [web.Worker] only. |
78 | | | @override |
79 | | 1 | void closeStream() => _postResponse(WorkerResponse.closeStream()); |
80 | | |
|
81 | | | /// Sends the [WorkerResponse] to the worker client. This method must be |
82 | | | /// called from the [web.Worker] only. |
83 | | | @override |
84 | | 0 | void error(Object err, [StackTrace? stackTrace, int? command]) { |
85 | | 1 | final ex = SquadronException.from(err, stackTrace, command); |
86 | | 1 | _postResponse(WorkerResponse.withError(ex)); |
87 | | 0 | } |
88 | | | } |
89 | | |
|
90 | | | /// Creates a [WorkerChannel] from a [web.MessagePort]. |
91 | | | @internal |
92 | | | WorkerChannel? deserialize(PlatformChannel? channelInfo, [Logger? logger]) => |
93 | | | (channelInfo == null) ? null : _WebWorkerChannel._(channelInfo, logger); |