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?
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 would like to make a helpfer method which takes a stream from a library (draw in this case) and changes the stream from Stream<UserContent> to Stream<Submission> and also filters the Submissions for duds.
So far I have this code but how do I return the stream now?
Stream<Submission> getSavedPosts({int limit = 20}) async* {
yield await _me!.saved(limit: limit).map((UserContent userContent) async {
try {
Submission sub = await _populateUserContent(userContent);
if (_filterPost(sub)) {
return sub;
}
} catch (err) {
print(err);
}
}); // The type 'Stream<Stream<Future<Submission?>>>' implied by the 'yield' expression must be assignable to 'Stream<Submission>'.
}
you should use asyncMap method on the stream rather than map.
Stream<ClassA> getOldStream() {}
Stream<ClassA> getNewStream() {
return getOldStream().asyncMap((event) async {
return event;
});
}
Note: yield and async* should be used in a generator function; mapping doesn't need a generator function
I have a class Too
class Too{
bool isLogged = false;
BehaviorSubject suject = BehaviorSubject<bool>();
Too({required this.isLogged}){
suject = new BehaviorSubject<bool>.seeded(isLogged);
}
void login(){
isLogged = true;
suject.sink.add(isLogged);
}
void logOut(){
isLogged = false;
suject.sink.add(isLogged);
}
void dispose(){
suject.close();
}
and I also have the Foo class:
class Foo{
Too _too = new Too(isLogged: false);
_too.stream.listen((event) { print('${event}');});
}
My issue is When the user is calling the login() method of the Too class nothing happens at the level of the Foo class.
What I want to do is that if the user calls the login() method of the Too class and his isLogged attribute is set to true, then this change is done at the level of all the classes that have an attribute of the Too type.
Note: It's much easier to do it with Angular or Ionic using RxJS, but with dart, I don't know how to implement this mechanism.
Foo is not reacting because its listening to a different instance of Too.
The way you have it is that each new instance of Foo creates a new instance of Too. If I understand you correctly, you want all instances of Foo to react to any change to a single instance of Too.
You can use a singleton for this.
class Too {
// one of a few ways to make a singleton in Dart
Too._();
static final _instance = Too._();
factory Too() {
return _instance;
}
final subject = BehaviorSubject<bool>.seeded(isLogged);
static bool isLogged = false;
void login() {
isLogged = true;
subject.sink.add(isLogged);
}
void logOut() {
isLogged = false;
subject.sink.add(isLogged);
}
void dispose() {
subject.close();
}
}
Now you can have any Foo object listen to the same Too instance.
class Foo {
Foo() {
Too().subject.stream.listen((event) {
print('foo $event'); // this will now print whenever a subject from your Too class is updated.
});
}
}
Now for example you could test this by creating a button with this as the onPressed.
onPressed: () {
final foo = Foo(); // just created an example of a Foo object that will
// print the updated value of the Too singleton
Too().login();
},
RxDart is great. However when it comes to reactive programming in Flutter, I suggest checking out Get X as it simplifies a lot of stream based stuff.
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).
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
}