Is it possible to pass an async method as an async call in Dart?
For example, if I want to simplify this code:
...
void myFunc() async {
await myLib.work();
}
...
RaisedButton(
onPressed: () async { await myFunc(); },
)
to this:
...
void myFunc() async {
await myLib.work();
}
...
RaisedButton(
onPressed: myFunc,
)
Passing myFunc just as a reference and still maintain that each time myFunc is called from onPressed as a callback, it will be an asynchronous call.
In short: Yes. The two pieces of code that you show are almost completely equivalent. Both call myLib.work and complete a future when the call's asynchronous computation completes.
You could probably also just write:
RaisedButton(
onPressed: myLib.work,
)
and it would still do the same thing.
However, I am not absolutely sure I understand what you mean by an "asynchronous call".
Dart function objects are not split into asynchronous functions and non-asynchronous functions that are called differently. They can be split into those returning Futures and those which don't, but you call them the same way. I assume that myLib.work returns a Future. You may then choose to await the result, but that's not part of the call.
Calling a function is not itself asynchronous, it's always synchronous - it returns a value before computation continues. That value might be a Future which will be completed later when an asynchronous computation has completed, but the function returns synchronously.
So, when you bind a void Function() method to the onPressed event, it will eventually call that function, ignore the return value, and continue synchronously.
If that function is myLib.work, then it calls myLib.work and ignores the returned Future. When the asynchronous work of work is done, it will complete the returned future, which nobody will notice.
If the function is myFunc, then it calls myFunc which immediately calls myLib.work and remembers the returned future. Then myFunc synchronously returns a new Future (which is ignored) and starts waiting (because of the await) on the future returned by myLib.work. When that future completes the await is done, and then the myFunc body is done, and finally the new Future is also completed. Again, nobody notices.
If the function is () async => await myFunc() then that function is called. It immediately calls myFunc, which immediately calls myLib.work, which returns a Future that myFunc starts waiting for and then returns a Future, which the function expression starts waiting for and then returns a Future, which is then ignored.
Eventually all these functions are completed, and nobody notices.
"If a future completes and nobody is listening, does it make an event?"
Related
Keeping a cron job pub/sub function (functions.pubsub.schedule), within a cloud function (functions.https.OnRequest) and exporting it, does not execute.
A complete example is as follows:
export const sayHelloWhen = functions.https.onRequest((request, response) => {
cors(request, response, () => {
const scheduleExpression = request.body.data.scheduleExpression;
functions.logger.log(`Called sayHelloWhen with ${scheduleExpression}`);
functions.pubsub.schedule(scheduleExpression).onRun((context) => {
functions.logger.log(`Executed sayHelloWhen with ${scheduleExpression}`)
});
response.send({
status: "success",
data: `scheduled at ${scheduleExpression}`
})
})
})
The problem is pub/sub does not trigger. Other codes are executed.
I would like to have HTTP request body scheduleExpression bring into pubsub.schedule's parameter. I don't want a static schedule expression in corn job.
In client, I would like to define a schedule expression in client side as follows:
function scheduleFunction() {
const functions = getFunctions();
const sayHello = httpsCallable(functions, "sayHelloWhen");
sayHello({ scheduleExpression: "every 1 minute" }).then((result) => {
// const data = result.data;
console.log("Result:", result);
});
}
The example below works only for a static schedule expression, meaning that a cloud function itself has a fixed schedule expression:
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
console.log('This will be run every 5 minutes!');
return null;
});
It can be exported as cron job trigger and it executes.
But keeping pub/sub cron job function, within onRequest cloud function, as in the first code example, does not execute.
I think it is very interesting what you are trying to do, but I would like to point out that you are missing some steps that could cause your app not to execute the way you need it to.
First, you need to Terminate your HTTP functions
Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might continue to run and be forcibly terminated by the system. See also Sync, Async and Promises.
When you do not terminate them, you might end up in a deeper level in which the following code will not execute unless the previous code has finished. I would also like to say I have not found any application with nested functions like you are doing.
In the Sync, async, and promises page you can find a very explicative video to understand the lifecycle of your functions, and in the How promises work with functions section we have:
When you return a JavaScript promise to a function, that function keeps running until the promise is resolved or rejected. To indicate that a function has completed its work successfully, the promise should be resolved. To indicate an error, the promise should be rejected. This means you only need to handle errors that you want to.
In addition to all this, I would suggest using a separate file to Organize multiple functions for a more organized code, but this is only a suggestion not really necessary.
Finally, I am concerned about the scheduledExpression parameter, I think if none of the above works, you might want to check and share what this value is.
How do I implement an async function that will check each result of multiple parallel async functions and cancels them all if a certain result is found (say a bool returns false). Otherwise it will let all the async functions complete then return the final result (say bool true). I have tried Async.Choice from the F# snippets site, but it uses Async.FromContinuations that will only let you call the continuation once.
I am relating to java as to how to do thread/async. I use new Thread(target).start() where target is Runnable as one way to do threading in java. New concurrent api has alternatives but we know that on specific call new threads are creating and passed in tasks are executed.
Similarly how is async done in Dart ?
I read on send/receivport, completer/future, spawnFunction. To me only spawnFunction is convincing statement that will create new thread. can one explain how completer/future help. i know they take callbacks but is there some implicit logic/rule in javascript/dart that callbacks always be execute in different thread.
below is dart snippet/pseudo code:
void callback() {
print("callback called");
}
costlyQuery(sql, void f()) {
executeSql(sql);
f();
}
costlyQuery("select * from dual", callback);
I hope my costlyQuery signature to take function as 2nd parameter is correct. so now I do not think that f() after executeSql(sql) is going to be async. may be taking above example add completer/future if that can make async to help me understand.
tl;dr: There is no implicit rule that a callback will not block.
Javascript event queue
In Javascript, there are no threads (except WebWorkers, but that's different). This means that if any part of your code blocks, the whole application is blocked. There is nothing magic about callbacks, they are just functions:
function longLoop(cb) {
var i = 1000000;
while (i--) ;
cb();
}
function callback() {
console.log("Hello world");
}
function fun() {
longLoop(callback);
console.log("Called after Hello World is printed");
}
To make something asynchronous, the callback must be put on the event queue, which only happens through some API calls:
user initiated event handlers- clicks, keyboard, mouse
API event handlers- XmlHTTPRequest callbacks, WebWorker communication
timing functions- setTimeout, setInterval
When the event is triggered, the callback is placed on the event queue to be executed when all other callbacks have finished executing. This means that no two lines of your code will ever be executing at the same time.
Futures
I assume you have at least stumbled on this post about futures. If not, it's a good read and comes straight from the horses mouth.
In Javascript, you have to do some work to make sense of asynchronous code. There's even a Javascript library called futures for doing common things, such as asynchronous loops and sequences (full disclosure, I have personally worked with the author of this library).
The notion of a future is a promise made by the function creating the future that the future will be completed at some point down the road. If you are going to make some asynchronous call, create a future, return it, and fulfill the promise when the asynchronous call finishes. From the blog post:
Future<Results> costlyQuery() {
var completer = new Completer();
database.query("SELECT * FROM giant_table", (results) {
// when complete
completer.complete(results);
});
// this returns essentially immediately,
// before query is finished
return completer.future;
}
If database.query is a blocking call, the future will be completed immediately. This example assumes that database.query is a non-blocking call (async), so the future will be fulfilled after this function exits. completer.complete will call whatever function is passed to completer.then() with the arguments specified.
Your example, modified to be idiomatic and asynchronous:
void callback() {
print("callback called");
}
costlyQuery(sql) {
var completer = new Completer();
executeSql(sql, () => completer.complete());
return completer.future;
}
costlyQuery("select * from dual").then(callback);
This is asynchronous and uses futures correctly. You could pass your own callback function into costlyQuery as you have done and call that in the callback to executeSql to achieve the same thing, but that is the Javascript way of doing it, not the Dart way.
Isolates
Isolates are similar to Java's threads, except that isolates are a concurrency model and threads are a parallelism model (see this SO question for more information about concurrency vs parallelism).
Dart is special in that the specification does not mandate that everything be run in a single thread. It only mandates that different executing contexts do not have access to the same data. Each isolate has it's own memory and can only communicate (e.g. send data) with other isolates through send/receive ports.
These ports work similarly to channels in Go if you're familiar.
An isolate is an isolated execution context that runs independently of other code. In Javascript terms, this means that it has it's own event queue. See the Dart tour for a more complete introduction to isolates.
Lets say your executeSql is a blocking function. If we don't want to wait for it to finish, we could load it into an isolate:
void callback() {
print("callback called");
}
void costlyQuery() {
port.receive((sql, reply) {
executeSql(sql);
reply.send();
});
}
main() {
var sendPort = spawnFunction(costlyQuery);
// .call does all the magic needed to communicate both directions
sendPort.call("select * from dual").then(callback);
print("Called before executeSql finishes");
}
This code creates an isolate, sends data to it then registers a callback for when it's done. Even if executeSql blocks, main() will not necessarily block.
I'm trying to grok how C# 5's new async feature works. Suppose I want to develop an atomic increment function for incrementing an integer in a fictitious IntStore. Multiple calls are made to this function in one thread only.
async void IncrementKey(string key) {
int i = await IntStore.Get(key);
IntStore.Set(key, i+1);
}
It seems to me that this function is flawed. Two calls to IncrementKey could get the same number back from IntStore (say 5), and then set it to 6, thus losing one of the increments?
How could this be re-written, if IntStore.Get is asynchronous (returns Task) in order to work correctly?
Performance is critical, is there a solution that avoids locking?
If you are sure you are calling your function from only one thread, then there shouldn't be any problem, because only one call to IntStore.Get could be awaiting at at time. This because:
await IncrementKey("AAA");
await IncrementKey("BBB");
the second IncrementKey won't be executed until the first IncrementKey has finished. The code will be converted to a state machine. If you don't trust it, change the IntStore.Get(key) to:
async Task<int> IntStore(string str) {
Console.WriteLine("Starting IntStore");
await TaskEx.Delay(10000);
return 0;
}
You'll see that the second Starting IntStore will be written 10 seconds after the first.
To quote from here http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx The “await” operator ... means “if the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then return to your caller immediately; the task will invoke the continuation when it completes.”
Based on Dart official page When using Async/Wait:
When the app sees the word async it execute the function normally synchronously, until it sees await or return
Note that an async function starts executing right away
(synchronously). The function suspends execution and returns an
uncompleted future when it reaches the first occurrence of any of the
following:
The function’s first await expression (after the function gets the
uncompleted future from that expression).
Any return statement in the function.
The end of the function body.
and when it sees any of them it returns an uncompleted Future and stops executing the async function until it execute all of the other functions, and when all of the other functions are executed, the app goes back to the async function and executes what's inside of it.
Here's a photo from the Dart official page explaining it in more details:
But when I was testing that I tried to add a print statement before returning the future result as you can see in the below code, but the result wasn't as stated in the site, as it's saying that the app stops executing once it sees the word awaitbut the statement: "Async - Hi Called 1st" was printed as you can see before the other functions were executed.
import 'dart:async';
Future<void> print1stAsync() async {
var test = await callAsync();
print(test);
}
main() {
print1stAsync();
print2nd();
print3rd();
print4th();
}
print2nd() {
print('Called 2nd');
}
print3rd() {
print("Called 3rd");
}
print4th() {
print('Called 4th');
}
Future<String> callAsync() {
print("Async - Hi Called 1st");
return Future(() => "Async - Called 1st ");
}
Output:
Async - Hi Called 1st
Called 2nd
Called 3rd
Called 4th
Async - Called 1st
So why is this happening? have I miss understood something?
The app doesn't stop executing, only the execution of the code after await is delayed until the returned Future completes.
You also need to await the call to the async function print1stAsync(), otherwise the execution of main continues after the call to callAsync(); (after the call, not after the Future it returns completes)
main() async {
await print1stAsync();
print2nd();
print3rd();
print4th();
}
Add async to a function also means that this function returns a Future.
There is no way to go back from async to sync. Async is contagious.
await callAsync(); means code below that line within the same function (like print(test); in your example) will be delayed.
It doesn't say anything about code in callAsync() or code that calls print1stAsync();