Overview
I have a function f1 which is non-async function.
f1 gets called multiple times and I have no control over the calling of f1
When f1 gets called I would like to invoke an async function f2
Aim:
I would like f2 to complete before the next f2 executes
Question:
How can I ensure f2 executes in sequence? (sample code below)
//I have no control over f1
//f1 can get called multiple times in quick succession.
func f1() {
Task {
try await f2() // Next time f1 gets called it should wait for the previous f2 to complete then should execute f2
}
}
func f2() async throws {}
Related
What's difference between Future.value vs Future.microtask
Case1:
Future.microtask(() => 1).then(print);
Future.microtask(() => Future(() => 2)).then(print);
Future.value(3).then(print);
Future.value(Future(() => 4)).then(print);
Output for this is:
1
3
4
2
Case2: And when I swaps statements
Future.value(3).then(print);
Future.value(Future(() => 4)).then(print);
Future.microtask(() => 1).then(print);
Future.microtask(() => Future(() => 2)).then(print);
output is:
3
1
4
2
Questions:
What's difference between Future.value vs Future.microtask?
Which of two has more priority? Whether Future.value completes first or Future.microtask?
Why the order of the final output (4 and 2) remains unchanged?
Can someone explain this behavior considering event and microtask queue?
Future.microtask schedules a microtask to execute the argument function. It then completes the future with the result of that function call.
Future() and Future.delayed schedules a timer task, the former with Duration.zero, to execute a function, and complete the future with the result of that function call.
Future.value takes a value, not a function to call. If you do Future.value(computation()), the computation is performed (or at least started, in case it's async) right now.
If you do Future.microtask(computation), the computation is started in a later microtask.
In each case, if the function returns a future or the value passed to Future.value is a future, then you'll also have to wait for that future to complete, before the future returned by the Future constructor is completed with the same result.
For the concrete example:
Future.value(3).then(print);
This creates a future completed with the value 3.
However, since futures promise to not call a callback, like then(print), immediately when the then is called, it schedules a microtask to actually call the print callback at a later time. So, you get an extra delay there.
In more detail:
Future.microtask(() => 1).then(print);
// This `Future.microtask(...)` creates future, call it F1,
// and schedules a microtask M1 to call `() => 1` later.
// Then adds callback C1 (`then(print)`) to F1, but F1 isn't completed yet,
// so nothing further happens.
Future.microtask(() => Future(() => 2)).then(print);
// Creates future F2 (`Future.microtask(...)`),
// schedules microtask M2 to run `() => Future(() => 2)` later,
// then callback C2 (`.then(print)`) to F2.
Future.value(3).then(print);
// Creates future F3 with value 3. Adds C3 (`then(print)`) to F3.
// Since F3 is complete, it schedules M3 to invoke C3.
Future.value(Future(() => 4)).then(print);
// Creates future F4 (`Future(() => 4)`)
// which starts *timer* T1 with duration zero to run `() => 4`.
// Then creates future F5 (`Future.value(...)`) with "value" F4.
// Completing with a future adds a callback C4 to F4,
// to notify F5 when a result is ready.
// Then adds callback C5 (`then(print)`) to F5.
That's what happens immediately. Then the event/microtask loop takes over.
Eventually M1 runs. This executes () => 1 to the value 1.
Then F1 is completed with the value 1.
Then F1 notifies all its existing callbacks, which invokes C1 with 1.
Which prints "1".
Then M2 runs. This evaluates Future(() => 2).
That creates future F6 (Future(...) and a timer T2 with duration zero.
It then completes F2 with the future F6,
which means adding a callback C6 to F6 to notify F2 of a result.
Then M3 runs. This invokes C3 with the value 3.
Which prints "3".
Now all microtasks are done.
Timer T1 runs which evaluates () => 4 to 4.
F4 completes with the value 4.
F4 calls its existing callbacks, C4 with 4.
That completes F5 with the value 4,
and calls its existing callback C5 with the value 4.
Which prints "4".
Timer T2 runs () => 2 and completes F6 with the value 2.
This runs F6's existing callback C6 with the value 2.
That callback completes F2 with the value 2,
and it calls F2's existing callback C2 with the value 2
Which prints "2".
So, three microtasks, two timers, and some future result propagation later, you get the result you see.
The second example can be done in the same way:
Future.value(3).then(print);
// Schedule a microtask to print 3.
Future.value(Future(() => 4)).then(print);
// Schedule a timer to (going through an extra future) print 4.
Future.microtask(() => 1).then(print);
// Schedule a microtask to compute and print 1.
Future.microtask(() => Future(() => 2)).then(print);
// Schedule a microtask to schedule a timer to eventually print 2.
The microtask-only ones, 3 and 1, should print first in order.
Then it should print 4, and then 2, because the 2-timer is scheduled after the 4-timer. 3-1-4-2, which is what you see.
Lua will write the code of a function out as bytes using string.dump, but warns that this does not work if there are any upvalues. Various snippets online describe hacking around this with debug. It looks like 'closed over variables' are called 'upvalues', which seems clear enough. Code is not data etc.
I'd like to serialise functions and don't need them to have any upvalues. The serialised function can take a table as an argument that gets serialised separately.
How do I detect attempts to pass closures to string.dump, before calling the broken result later?
Current thought is debug.getupvalue at index 1 and treat nil as meaning function, as opposed to closure, but I'd rather not call into the debug interface if there's an alternative.
Thanks!
Even with debug library it's very difficult to say whether a function has a non-trivial upvalue.
"Non-trivial" means "upvalue except _ENV".
When debug info is stripped from your program, all upvalues look almost the same :-)
local function test()
local function f1()
-- usual function without upvalues (except _ENV for accessing globals)
print("Hello")
end
local upv = {}
local function f2()
-- this function does have an upvalue
upv[#upv+1] = ""
end
-- display first upvalues
print(debug.getupvalue (f1, 1))
print(debug.getupvalue (f2, 1))
end
test()
local test_stripped = load(string.dump(test, true))
test_stripped()
Output:
_ENV table: 00000242bf521a80 -- test f1
upv table: 00000242bf529490 -- test f2
(no name) table: 00000242bf521a80 -- test_stripped f1
(no name) table: 00000242bf528e90 -- test_stripped f2
The first two lines of the output are printed by test(), the last two lines by test_stripped().
As you see, inside test_stripped functions f1 and f2 are almost undistinguishable.
I have a list of Mono to run, but for memory consumption concern I'd like them to run one after another:
var monos = [m1, m2, m3];
Mono.zip(monos, (results) -> {
// handle result
});
The code above would execute m1 m2 m3 in parallel, how to control them in sequence and collect results?
To run the Monos one after another in sequence, you can put your Monos in a Flux and call Flux.concatMap. To collect the results, you can use Flux.collectList to obtain a Mono<List<T>> that emits the list after the last Mono is finished:
var monos = List.of(m1, m2, m3);
Flux.fromIterable(monos)
.concatMap(mono -> mono)
.collectList()
.map(results -> ...) // handle results as a List<T>
I figured out to use:
Flux([1, 2, 3]).concatMap(...) to solve this problem. Not sure if there are better way to do that.
I am trying to understand how JavaRx's Flux.merge and switchIfEmpty work together in regards to the below code as I am a bit confused on results I am seeing which is no doubt the result of my not fully grasping Java RX.
My question is ... If the call to wOneRepository... returns an empty list or the call to wTwoRepository... returns an empty list, will the switchIfEmpty code get executed? Or will it only get executed if both calls return an empty list?
Flux<Widget> f1 = wOneRepository.findWidgets(email).onErrorResume(error -> Flux.empty());
Flux<Widget> f2 = wTwoRepository.findWidgets(email).onErrorResume(error -> Flux.empty());
return Flux.merge(f1, f2)
.switchIfEmpty(Flux.error(new IllegalArgumentException("Widgets were not found")));
Thank you
switchIfEmpty() will only be called if the upstream Flux completes without emitting anything, and that will only happen if both f1 and f2 complete without emitting anything. So, if both findWidget calls fail, or both return empty Flux instances, or some combination of those, then switchIfEmpty will be called. If either f1 or f2 emits a Widget, then that Widget will be emitted from the merge operator, which means switchIfEmpty will not be called.
I am learning lua by this video tutorial, it has this piece of code:
co = coroutine.create(function()
for i=1,5 do
print(coroutine.yield(i))
end
end)
print(coroutine.resume(co,1,2))
print(coroutine.resume(co,3,4))
print(coroutine.resume(co,5,6))
print(coroutine.resume(co,7,8))
print(coroutine.resume(co,9,10))
print(coroutine.resume(co,11,12))
The output is like this:
true 1
3 4
true 2
5 6
true 3
7 8
true 4
9 10
true 5
11 12
true
But I don't understand how yield and resume passes parameters to each other and why yield doesn't output the first 1,2 that resume passes to it, could someone please explain? Thanks
Normal Lua functions have one entry (where arguments get passed in) and one exit (where return values are passed out):
local function f( a, b )
print( "arguments", a, b )
return "I'm", "done"
end
print( "f returned", f( 1, 2 ) )
--> arguments 1 2
--> f returned I'm done
The arguments are bound to the parameter names (local variables) by putting them inside the parentheses, the return values listed as part of the return statement can be retrieved by putting the function call expression in the right-hand side of an assignment statement, or inside of a larger expression (e.g. another function call).
There are alternative ways to call a function. E.g. pcall() calls a function and catches any runtime errors that may be raised inside. Arguments are passed in by putting them as arguments into the pcall() function call (right after the function itself). pcall() also prepends an additional return value that indicates whether the function exited normally or via an error. The inside of the called function is unchanged.
print( "f returned", pcall( f, 1, 2 ) )
--> arguments 1 2
--> f returned true I'm done
You can call the main function of a coroutine by using coroutine.resume() instead of pcall(). The way the arguments are passed, and the extra return value stays the same:
local th = coroutine.create( f )
print( "f returns", coroutine.resume( th, 1, 2 ) )
--> arguments 1 2
--> f returns true I'm done
But with coroutines you get another way to (temporarily) exit the function: coroutine.yield(). You can pass values out via coroutine.yield() by putting them as arguments into the yield() function call. Those values can be retrieved outside as return values of the coroutine.resume() call instead of the normal return values.
However, you can re-enter the yielded coroutine by again calling coroutine.resume(). The coroutine continues where it left off, and the extra values passed to coroutine.resume() are available as return values of the yield() function call that suspended the coroutine before.
local function g( a, b )
print( "arguments", a, b )
local c, d = coroutine.yield( "a" )
print( "yield returned", c, d )
return "I'm", "done"
end
local th = coroutine.create( g )
print( "g yielded", coroutine.resume( th, 1, 2 ) )
print( "g returned", coroutine.resume( th, 3, 4 ) )
--> arguments 1 2
--> g yielded true a
--> yield returned 3 4
--> g returned true I'm done
Note that the yield need not be directly in the main function of the coroutine, it can be in a nested function call. The execution jumps back to the coroutine.resume() that (re-)started the coroutine in the first place.
Now to your question why the 1, 2 from the first resume() doesn't appear in your output: Your coroutine main function doesn't list any parameters and so ignores all arguments that are passed to it (on first function entry). On a similar note, since your main function doesn't return any return values, the last resume() doesn't return any extra return values besides the true that indicates successful execution as well.
co = coroutine.create(function()
for i=1,5 do
print(coroutine.yield(i))
end
end)
We start the coroutine the first time using:
print(coroutine.resume(co,1,2))
it will run until the first yield. our first resume call will return true and the parameters of yield (here i = 1) which explains the first output line.
our coroutine is now suspended. once we call resume a second time:
print(coroutine.resume(co,3,4))
your first yield finally returns and the parameters of your current resume (3,4) will be printed. the for loops second iteration begins, coroutine.yield(2) is called, supending the coroutine which again will make your last resume return true, 2 and so on
So actually in your example coroutine.resume(co) would be sufficient for the first call as any further arguments are lost anyway.
The reason we see this behavior is subtle, but it has to do with a mismatch of "entering" and "exiting" yield statements. It also has to do with the order in which print and yield are called within your anonymous function.
Let's imagine a graph of the execution of print(coroutine.yield(i)) vs. iteration.
On the first iteration, we have coroutine.resume pass 1 and 2 to the coroutine. This is the origin point, so we are not picking up from a former yield, but rather the original call of the anonymous function itself. The yield is called inside the print, returning i=1 but leaving print uncalled. The function exits.
What follows is the suspension of the function for a time before we see resumption by the next coroutine.resume.This resume passes 3 and 4. The function picks up at the last yield. Remember that the print function was left uncalled as the 1st yield was called first and exited the program? Well, execution returns inside the print, and so print now gets called, but this time returning 3 and 4 as these were the latest values to be transferred over. The function repeats again, calling yield before print, returning i=2.
If we go down the iterations we will more definitely the pattern underlying why we didn't see the 1 and 2. Our first iteration was an "exiting" yield unpaired with a corresponding "entering yield." This corresponds to the 1st time execution of the coroutine co.
We might expect the last yield to also go unpaired, but the difference is that we'll have an "entering" yield which is unpaired as opposed to an "exiting" yield which is unpaired. This is because the loop would have already finished. This explains why we see 11 12 followed by true followed with no "exiting" yield return.
This sort of situation is independent of the parity (even/oddness) of the for loop inside. What only matters are the resume and yield pairs and the manner in which they are handled. You have to appreciate that yield won't return a value within the function on the first call of resume which is being used to call the function inside the coroutine in the first place.