3 ms·
> as long as you only use async/await... Well, what if you don't? That's the point. The proposed construct does not require that: I'm not saying its better o
by wokwokwok 9y ago
> as long as you only use async/await...
Well, what if you don't?
That's the point.
The proposed construct does not require that: I'm not saying its better or worse; I'm just pointing out that it's different.
Obviously if you choose to avoid branching code in your functions, they won't branch; what's novel here is that the closing scope (ie. in this case, the collapse of the python with block) triggers a collection of all the ambient tasks started in that context.
I'm sure you could implement something similar in javascript, but it is not the same as Promise.all.
- joshuamorton 9y ago>Well, what if you don't? This is kind of a silly question. Its always possible to subvert a safe construct system if you try hard enough. You can write unsafe blocks in rust. You can pass in a callback to a nursery, as is described in the article. >The proposed construct does not require that Well, kind of. The article actually explicitly states that >Here's a simpler primitive that would also satisfy our flow control diagram above. It takes a list of thunks, and runs them all concurrently: which is equivalent to promise.all, satisfies the invariant nurseries do, except in very specific circumstances (unbounded while loop-y constructs). And you just simply can't use promise.all in that situation.
- wokwokwok 9y agoOh come on. I don't agree that the proposed idea is fundamentally new and amazing; but I think it is novel, and there may be some value in being able to semantically bind tasks to execution points, specifically when the tasks are spawned in naive (or uncontrolled, eg. library) code, and have not explicitly opted in to the scheme. Does this particular implementation do that perfectly? No, probably not. ...but I think the idea of it has some merit. There's more value here than just waiting for a series of deferred tasks that you have explicitly created, and explicitly opted into a specific concurrency workflow with. Note specifically these parts of the proposal: > we declare that a parent task cannot start any child tasks unless it first creates a place for the children to live ^-- This is not satisfied by your method, at all. > But the problem with this is that you have to know up front the complete list of tasks you're going to run, which isn't always true. Literally the next sentence after the one you quoted. > What if you really do need to write a function that spawns a background task, where the background task outlives the function itself? Easy: pass the function a nursery object. There's no rule that only the code directly inside the Notice how you can avoid the 'setTimeout(() => { .. })` in nested code issue by explicitly passing in a context that has its own scope. ... There's a lot of stuff in here that is actually quite thoughtful. You don't have to dig through it, finding every little nitpick you can to call it out on; just think about the idea being proposed. It's certainly not 'just promises'; if you think it is, then... I don't know what to say. You're wrong. :P
- joshuamorton 9y ago>and have not explicitly opted in to the scheme. I think you're overestimating nurseries here. They still don't handle the case you described of a deeply nested setTimeout: import asyncio import trio import threading async def my_tricky_function(): threading.Timer(5, lambda: print('first')).start() await trio.sleep(.5) async def a_function(): async with trio.open_nursery() as n: n.start_soon(my_tricky_function) print("finished") trio.run(a_function) (requires python 3.6, I'm using threading.timer in place of setTimeout, but they're the same construct). There's a great talk by david beazly about how threads and asyncio don't play nicely, except when they do. Note that you could imagine that this timer is created in an awaited function or a supposedly synchronous child function. If the nursery did what you think it did, which is to wait until everything async within the block completes, you'd see this print first, then print finished. It doesn't though, finished is printed first. The reason is that you need to opt in to the trio constructs by using promises/futures/coroutines. Using threaded callback based things give you the same problems with trio as they do in other async/parallel constructs. If you're a good citizen and opt in to the safety guarantees trio provides, it can keep things clean for you, yes. But you do need to explicitly opt into the scheme by only using code that you know is promise/future/coroutine based. Threads are a doozy. But the same thing is pretty much true with async/await promises/coroutines in js or python. If you require that you only use promises (or you tightly wrap all of your threads/callbacks in promises and then use that), you can get the safety of nurseries in most of the situations (I noted there were some exceptions before!), but with just the async/await syntax, no need for the extra async context manager. I agree that trio is cool. I've used it before. But you're ascribing to it and this construct magical powers that they cannot and do not have.
- wokwokwok 9y ago> they cannot and do not have. sigh~ In this implementation, or perhaps, in python at all... Does that make it completely valueless? I would venture to suggest that maybe there's a big wide world of languages that actually support controlling how threads are spawned, where it might not be. Maybe its worth considering.