The UI shows two button: One button to start an isolate, and a second button to stop it at next occasion.
The UI (widget) code looks like this:
SendPort sendToIsolatePort;
void _onStartIsolateButtonPushed() async {
ReceivePort receivePort = ReceivePort();
receivePort.listen(onMessageReceivedFromIsolate);
Isolate.spawn(runAsIsolate, receivePort.sendPort);
}
void _onStopIsolateButtonPushed() async {
sendToIsolatePort.send("Stop");
}
void onMessageReceivedFromIsolate(var message) {
if (message is String) {
print("Message received from isolate: " + message);
} else if (message is SendPort) {
print("Reply port received");
sendToIsolatePort = message;
sendToIsolatePort.send("Hello World?!?");
}
}
The code in isolate.dart looks like this:
(Note: this one's not in a widget or class, just some global functions)
import 'dart:isolate';
SendPort sendPort;
bool isRunning;
void runAsIsolate(SendPort port) async {
sendPort = port;
ReceivePort receivePort = ReceivePort();
receivePort.listen(onIsolateMessageReceived);
isRunning = true;
sendPort.send(receivePort.sendPort);
while (isRunning) {
_doSomething();
_doSomethingMore();
}
receivePort.close();
sendPort.send("Stopped");
print("Leaving isolate...");
}
void onIsolateMessageReceived(var message) {
if (message is String) {
print("Isolate: messate received: " + message);
if (message == "Stop") {
isRunning = false;
}
} else {
print("WTFlutter... " + message.toString());
}
}
void _doSomething() {}
void _doSomethingMore() {}
Now, for some reason, the isolate neither receives the "Hello World?!?" nor the "Stop" message. Do you have any ideas why? And how to fix it?
Also: Is there a simpler (or shorter) way of executing a thread in flutter? The isolate approach, and it's stream communication, appear very much over-complicated for something as common as parallel execution.
Your advise is much appreciated. Thank you.
(Rephrased from the comments above, since this is the answer that solves the question.)
I see a problem: your while (isRunning) will consume all time in our main isolate. thus there is no time that your onIsolateMessageReceived can be called! Try to remove your while loop, and do this instead: while(isRunning) await Future.delayed(Duration(seconds: 1));.
As for executing a thread: No, there is nothing else. isolates are the fundamental building blocks in flutter, and that is the design - no shared memory. However, there do exist some small shortcuts. For example, have a look at: api.flutter.dev/flutter/foundation/compute.html , which is a easier way to do computations in another "thread"(isolate).
Related
I would like to load some Data before I Render my Blazor Application because in depndency to the loaded data I would like to render my app (layout, navbar ...)
Now I want to use the OnInitialised method instead of OnInitialisedAsync and with no async and await keywords.
But now I had a problem to convert the data which I get back from my API.
protected override void OnInitialized()
{
try
{ Console.WriteLine("Test1Mainasync");
LoadCategories();
}
catch (Exception e)
{
jsRuntime.ToastrError(e.Message);
}
}
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
SD.Categories = CategoriesInit1.ToList();
//foreach(var categorie in CategoriesInit){
// SD.Categories.Append(categorie);
//}
Console.WriteLine("Test1Main");
}
Has someone an idea why this converting issues happen?
I think you have this method:
public async Task<IEnumerable<CategorieDTO>> GetAllCategories()
and you should call it this way:
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
and:
protected override async Task OnInitializedAsync()
{
try
{ Console.WriteLine("Test1Mainasync");
await LoadCategories();
}
Has someone an idea why this converting issues happen?
In your code CatagiesInit1 is a Task, it's not a List<CategorieDTO>. You only get the List<CategorieDTO> when the task completes which you have no control over as you don't await the completion of the Task. In all likelyhood, your sync code will run to completion before that happens.
If your CategoryService returns a Task then the code that handles it must be async code. You can't escape from the async world back into the sync world without consequencies. If you want to live in the sync world then all the data pipeline code also needs to be blocking sync code.
If I understand your comments correctly, you want nothing to render until a certain set of conditions are met. If so add some standard Loading... component code to the page if it's page specific or App.razor if it's on initial load, or say MainLayout if it's application wide.
Here's a quick an dirty example:
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
#if (Loaded)
{
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
}
else
{
<div class="m-2 p-5 bg-secondary text-white">
<h3>Loading.....</h3>
</div>
}
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
#code {
private bool Loaded;
protected override async Task OnInitializedAsync()
{
Loaded = false;
// simulate getting the data first
await Task.Delay(5000);
Loaded = true;
}
}
Your call to API endpoint return an awaitable task but not the IEnumerable, So you can not assign awaitable task to IEnumerable so this piece of code wont work
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
}
You should have your LoadCategories function like this
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
}
API calls should be awaitable, else it will stuck your UI
You can use this solution as well
private void LoadCategories()
{
var t = Task.Run(() => categorieService.GetAllCategories()()).GetAwaiter();
t.OnCompleted(() =>
{
CategoriesInit1 = t.GetResult();
// you may need to call statehaschanged as well
StateHasChanged();
});
}
I'm trying to add some file logging to code invoked by the Workmanager package. The callback has the general structure of the callback routine starting another async task, viz:
Future<void> callbackDispatcher() async {
.
.
.
Workmanager.executeTask((task, inputData) async {
.
.
.
return Future.value(true);
});
.
.
.
}
At the conclusion of whichever of these two routines finishes last, iiuc, I need to do:
iosink.close();
await iosink.done;
before I can exit/return.
As dart doesn't have destructors, I'm not sure how best to accomplish this. I've written the following which, while it seems to work, is rather fragile and somewhat hokey:
class LogWriter2 {
static final File _callbackLogFile = getLocalFile( "callbackLog.out" );
static IOSink _writer;
static int _instanceCount = 0;
LogWriter2._privateConstructor() {
//_writer = _callbackLogFile.openWrite(mode: FileMode.append);
}
static final LogWriter2 _instance = LogWriter2._privateConstructor();
factory LogWriter2() {
if (_instanceCount <= 0) {
debugPrint("${dt.now} constr: opening writer...");
_writer = _callbackLogFile.openWrite(mode: FileMode.append);
}
_instanceCount++;
debugPrint("${dt.now} constr: instanceCount++ now $_instanceCount");
return _instance;
}
writeln(String string) {
var dtn = dt.now;
_writer.writeln("${dtn} $string");
debugPrint("${dtn} lw2.writeln: $string");
}
close() async {
_instanceCount--;
debugPrint("${dt.now} close: instanceCount-- now $_instanceCount");
if (_instanceCount <= 0) {
debugPrint("${dt.now} close: closing writer...");
await _writer.flush(); // close() does not guarantee flush!
_writer.close();
await _writer.done;
debugPrint("${dt.now} close: close complete");
}
}
}
// nb. Don't use this code as-is as it has a race condition that is fixed
// with a mutex (omitted here). Message me if you need a working version.
Can someone suggest a better way of achieving this?
Vert.x v3.5.1.
There is my custom start method of Verticle:
#Override
public void start(Future<Void> startFuture) throws Exception {
startFuture.setHandler(event -> {
if (event.succeeded()) {
logger.info("Server started on port: {}", 8080);
} else {
logger.warn("Failed to start: {}", event.cause());
}
});
vertx.createHttpServer()
.requestHandler(router()::accept)
.listen(8080, event -> {
if (event.succeeded()) {
startFuture.complete();
} else {
startFuture.fail(event.cause());
}
});
}
I expect that my custom handler will be invoked when future completes. But it doesn't!
In debug mode I see that FutureImpl::setHandler is called twice: once with my custom handler, and then with DeploymentManager's handler during doDeploy.
So the latest handler is applied.
The question is: is it possible to specify custom callback for Verticle start? If yes how can I do it?
Thank you in advance.
You're not supposed to change the handler of the startFuture, as it is owned by the caller. So simply, don't do that :) The future is used to signal that you're done with your start-code, and not to define what should be done, once you're done.
In your concrete example it'd be better to write the logs once the http server came up, and then indicate to the startFuture, that you're done with your code execution.
#Override
public void start(Future<Void> startFuture) throws Exception {
vertx.createHttpServer()
.requestHandler(router()::accept)
.listen(8080, event -> {
if (event.succeeded()) {
logger.info("Server started on port: {}", 8080);
startFuture.complete();
} else {
logger.warn("Failed to start: {}", event.cause());
startFuture.fail(event.cause());
}
});
}
At first, write your verticle as a AbstractVerticle extension. And do not override start(Future) method. Instead use start() method. It will be called from AbstractVerticle.start(Future), and you can specify that future in Vertx.deployVerticle(verticle, options, future) method.
For example:
You have some verticle. Do in it start() method only initialization tasks:
#Override
public void start() throws Exception {
vertx.eventbus().consume(address, m -> {
// consumer code skipped
});
}
and verticle registration:
final DeploymentOptions opts = new DeploymentOptions().setWorker(true);
vertx.deployVerticle(verticle, opts, event -> {
if (event.succeeded()) {
log.info("Verticle successfully deployed. DeploymentId: " + event.result());
} else {
log.error("Verticle failed to deploy. Cause: " + event.cause().getMessage(), event.cause());
}
});
That's all :)
I have two operations step_1() and step_2() and want to execute step_2() AFTER step_1().
With normal java this would be:
step_1();
step_2();
With vertx I have to use vertx-compose(). Am I right?
According to https://groups.google.com/forum/#!topic/vertx/FuvlPLpoGOA, I dont need Futures for sequential code.
"If you want to do each request sequencially you dont need futures."
So how can I do that without using futures?
I dont know, if this matters: My Vertx from which this code is executed is a "Worker"-Verticle.
#Override
public void start(Future<Void> fut) throws IOException {
Future<Void> step_1 = Future.future();
step_1.compose(res -> {
// If the future succeeded
Future<Void> step_2 = step_1();
step_2.compose(res2 -> {
step_2();
}, Future.future().setHandler(handler -> {
// If the future failed
}));
//I dont need that
}, Future.future().setHandler(handler -> {
// If the future failed
}));
}
public void step_1(){
..
}
public void step_2(){
..
}
Is this the right and shortest (!) way?
Below is an example of chaining of Future, I have made the example very trivial nonetheless it showcases the concept.
#RunWith(VertxUnitRunner.class)
public class Chaining {
private Vertx vertx = Vertx.vertx();
#Test
public void futures_chaining(TestContext context) throws Exception {
Async async = context.async();
firstOperation()
.compose((outcome) -> {
System.out.println(outcome);
return secondOperation();
})
.compose(outcome -> {
System.out.println(outcome);
/*
For stopping unit test we are returning this future
for production use-case this would be Future.succeededFuture
or Future.failedFuture depending on your method outcomes
*/
return Future.future(handle -> async.complete());
});
}
private Future<String> firstOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("First Operation Complete"));
return future;
}
private Future<String> secondOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("Second Operation Complete"));
return future;
}
}
"If you want to do each request sequencially you dont need futures."
No, it's not. In asynchronous frameworks like Vert.x, input/output operations are non-blocking. It means, that if you call few asynchronous operations, they'll start working simultaneously. And if you want to do few requests sequentially, then you should use futures or callbacks to execute new request only after previous one finished successfully.
Check this code with futures, newer version with RxJava 2 and article about project.
#Override
public Future<Optional<Todo>> getCertain(String todoID) {
Future<Optional<Todo>> result = Future.future();
redis.hget(Constants.REDIS_TODO_KEY, todoID, res -> {
if (res.succeeded()) {
result.complete(Optional.ofNullable(
res.result() == null ? null : new Todo(res.result())));
} else
result.fail(res.cause());
});
return result;
}
#Override
public Future<Todo> update(String todoId, Todo newTodo) {
return this.getCertain(todoId).compose(old -> {
if (old.isPresent()) {
Todo fnTodo = old.get().merge(newTodo);
return this.insert(fnTodo)
.map(r -> r ? fnTodo : null);
} else {
return Future.succeededFuture();
}
});
}
RxJava exists specifically to compose async events: http://vertx.io/docs/vertx-rx/java/
Assuming both step_1() and step_1() aren't designed to return results (i.e. they effectively return void) then you could change them to return Observable or Single and chain them together similar to this:
step_1().doOnSuccess(this::step_2()).subscribe(/* control resumes here */);
RxJava (or rather, reactive programming in general) takes a little bit to wrap your head around it, but I would strongly recommend using it if you're planning to chain together async operations.
Pass step_2 as argument to step_1
#Override
public void start(Future<Void> fut) throws IOException {
step_1(step_2);
}
private void step_1(Runnable function){
someAsynccall("some-arg", response -> {
function.run();
}).end();
}
private void step_2(){
// do something
}
How do I block the current thread until the OnComplete handler of my observer has finished, without the use of threading primitives?
Here is my code. I want that the Console.WriteLine("Press... statement should be executed only after the OnComplete handler, namely ResetCount has finished executing.
class Program
{
private static long totalItemCount = 0;
private static long listCount = 0;
static void Main()
{
Console.WriteLine($"Starting Main on Thread {Thread.CurrentThread.ManagedThreadId}\n");
var o = Observable.Timer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(1))
.Take(20)
.Concat(Observable.Interval(TimeSpan.FromSeconds(0.01)).Take(200))
.Buffer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
o.Subscribe(Print, onCompleted: ResetCount);
// How I make sure this line appears only after the OnComplete has fired?
// Do I have to use traditional threading primitives such as wait handles?
// Or just cause the main thread to sleep long enough? That doesn't seem right.
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
private static void ResetCount()
{
if (listCount > 0)
{
Console.WriteLine($"{totalItemCount} items processed in {listCount} lists.");
}
else
{
Console.WriteLine($"{totalItemCount} items processed.");
}
Interlocked.Exchange(ref totalItemCount, 0);
Interlocked.Exchange(ref listCount, 0);
}
static void Print<T>(T value)
{
var threadType = Thread.CurrentThread.IsBackground ? "Background" : "Foreground";
if (value is IList)
{
var list = value as IList;
Console.WriteLine($"{list.Count} items in list #{Interlocked.Increment(ref listCount)}:");
foreach (var item in list)
{
Console.WriteLine($"{item.ToString()}, ({threadType} #{Thread.CurrentThread.ManagedThreadId}), Item #{Interlocked.Increment(ref totalItemCount)}");
}
Console.WriteLine();
}
else
{
Console.WriteLine($"{value.ToString()}, ({threadType} #{Thread.CurrentThread.ManagedThreadId}), Item #{Interlocked.Increment(ref totalItemCount)}");
}
}
}
On Rx we have specific schedulers to handle threading, synchronization and related.
You can read more about that here:
http://www.introtorx.com/content/v1.0.10621.0/15_SchedulingAndThreading.html
But basically what you're looking for is changing this line:
.Buffer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), Scheduler.CurrentThread);
They're several ways to test/validate a Rx query. Keep in mind that wouldn't be the answer for all the problems.