Git Product home page Git Product logo

Comments (8)

jfhbrook avatar jfhbrook commented on June 29, 2024

Events are generally fire-and-forget, so it doesn't really make sense to have any sort of asynchronous-specific support. A function is a function. You can call it whenever you want, and deal with the fallout how you will. Moreover, this is a pretty cut-and-dry implementation of nodejs-style EEs, and I don't really want to stray too far from that.

In other words, I'm not really interested in supporting any features beyond what's already implemented, but I can still talk use case with you, if you think I might have a suggestion in terms of alternate patterns.

from pyee.

jfhbrook avatar jfhbrook commented on June 29, 2024

(There's also the odd chance that I'm missing something due to unfamiliarity with how python implements futures/etc)

from pyee.

jAlpedrinha avatar jAlpedrinha commented on June 29, 2024

Not sure with what you mean with fire-and-forget, since EventEmitter has synchronous behaviour.
emit only returns execution when all handlers are called instead of using something like yield from handler which would allow to really fire the event and forget what will happen next, without having to wait for the handler to end execution.

I understand that this has a natural solution in nodejs by using setImmediate, which isn't as natural in python, since there isn't by default an event loop.

If you feel like sticking to EventEmitter like it exists in nodejs, there's no problem.

from pyee.

jfhbrook avatar jfhbrook commented on June 29, 2024

I don't follow. What I mean is that no result from any actions your EE takes are retained, so the function handler can easily kick off an asynchronous action no problem. Like in javascript you would just do

ee.on('event', (data) => {
  // This schedules an async action, but the actual meat of this function beyond validation
  // happens on a subsequent tick, and the EE's handler doesn't do anything with the
  // result (this is what I mean by "fire and forget")
  doSomeAsyncThing((err) => {
    if (err) throw err; // Whatever
    console.log('did the thing');
  });
});

and you're off to the races! I have to assume that in python you would do something similar. Looking at [https://docs.python.org/3/library/asyncio-task.html#example-hello-world-coroutine](python 3's docs for asyncio) I assume you'd just make some calls to loop with some coroutines.

But, out of curiosity, what would an example of your proposal Look Like? Maybe seeing a code sample will make this make more sense.

from pyee.

jAlpedrinha avatar jAlpedrinha commented on June 29, 2024

I'll provide you an example and explain the nature of async and await.

import asyncio
import pyee

ee = pyee.EventEmitter()

async def sleep(seconds):
  print('abc')
  await asyncio.sleep(seconds)
  print('dow')

ee.on('test', sleep)

async def start():
  print("I will emit")
  ee.emit('test', 2)
  print("I have emitted")
  await asyncio.sleep(10)

loop = asyncio.get_event_loop()
loop.set_debug(True)

loop.run_until_complete(start())
print ('done')

If you run this snippet you can see that you'll receive an error because I've set_debug to True on the event loop, if you disable you'll just get the same message as a warning.

The reason for this is that an async function is a coroutine which returns the execution to its caller after it is invoked returning a future. If this future is not awaited (or yielded) the code will simply not run.

This doesn't need to occur imediately, you can retrieve the future and wait for it later on, even though the stream of execution will temporarily stop (which seems like synchronous code) the reality is that if there is any other stream of execution running concurrently it will be able to continue while this is waiting.

This is highly similar to the ES proposal here

My proposal to change this would be to have an emit_async that would be a coroutine that would await handlers instead of just invoking them, that could itself be awaited. There would also be some changes on the once wrapper that would still work as it does now, but would behave differently when the handler is a coroutine.

I understand if you don't want to pursue this path, and in that case I will be more than happy to implement a version of this code. I'm just sharing this because I really love the observer pattern, and this was one of the cleanest implementations of it that I've found. But since I'm embracing the asynchronous side of python, which makes sense but breaks with some of the current establishment, I'd like to contribute with your effort.

from pyee.

jfhbrook avatar jfhbrook commented on June 29, 2024

So what you're saying is that we'd "like" to see the following (because async/await -> futures is the async io abstraction in python)

I will emit
abc
I have emitted
dow
done

but right now it prints

I will emit
abc
dow
I have emitted
done

?

Is there a way to switch on whether something is a couroutine/future-generating-function rather than a Boring Function, so that we don't need an alternate implementation? Is there a way to explicitly make it so you don't have to await (or maybe even can't await) the ee call (it sounds like you might have to in this proposal)?

You made comparisons to JavaScript's async/await, which I believe behind the scenes are just packing/unpacking promises (and yield/generators with iterators). Arguably, shouldn't your function handlers just be more explicit about this? I haven't heard of anyone making similar proposals for node, and promisified-by-default core modules are certainly being discussed.

from pyee.

jfhbrook avatar jfhbrook commented on June 29, 2024

This looks relevant http://stackoverflow.com/questions/37278647/fire-and-forget-python-async-await

from pyee.

jfhbrook avatar jfhbrook commented on June 29, 2024

I want to use https://docs.python.org/3.5/library/types.html#types.CoroutineType to switch on whether @ee.on is decorating a coroutine or a regular function (note: do I want to switch on generators for completeness?), and then use https://docs.python.org/3/library/asyncio-task.html#asyncio.ensure_future (or possibly an overridable version of this?) with a possible override to schedule coroutines to run.

Something like,

@ee.on('fire')
async def fire_and_forget():
    await async_things()

// from somewhere
@ee.emit('fire')

loop.close()

I think? Still learning how the event loop works.

from pyee.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.