Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async API #26

Open
fatcerberus opened this issue Oct 31, 2016 · 8 comments
Open

Async API #26

fatcerberus opened this issue Oct 31, 2016 · 8 comments

Comments

@fatcerberus
Copy link
Contributor

Sphere's internal event loop has long been exposed to the API in a rudimentary capacity. Sphere 1.x has SetUpdateScript() and SetRenderScript(), which only work in the map engine. minisphere brought DispatchScript() to the table, renamed to system.dispatch() in minisphere 4.0, which queues a function to be called on the next engine tick (in practice, on the next FlipScreen()). Async has always been kind of an afterthought in Sphere, because Sphere games have historically been written like C--largely sequential, often with independent "event loops" intermingled with game logic.

If we want to move Sphere forward though, we have to start thinking in terms of async and a central event loop. Platforms like the Web are async-only, and a system like Sphere 1.x would never work in a browser, as has been brought up many times before on the forums.

For my part, I'm implementing an API in minisphere which expands on system.dispatch() to allow more control over the event queue. For example, you'd be able to set up recurring jobs (such as update and render code), and later cancel those if needed.

This issue is to discuss alternatives for a standardized API for such a system. Developers would be able to use such a system to write code which would work both in minisphere as well as a potential "Web Sphere" which implemented the same API, with no changes.

@fatcerberus
Copy link
Contributor Author

Current design used in minisphere is:

job = Dispatch.now(asyncFunc);  // one-shot, from message pump
job = Dispatch.onUpdate(func);  // recurring, once per frame
job = Dispatch.onFlip(func);  // recurring, on flipscreen

And to stop a pending job from running:

job.cancel();

@joskuijpers
Copy link
Contributor

How about:

let job = new Job(func);
Dispatch.now(job)
job.cancel() ?

also, replace now with once, as it shouldn’t actually execute now but in the next tick.

On 01 Nov 2016, at 21:46, Bruce Pascoe [email protected] wrote:

Current design used in minisphere is:

job = Dispatch.now(asyncFunc); // one-shot, from message pump
job = Dispatch.onUpdate(func); // recurring, once per frame
job = Dispatch.onFlip(func); // recurring, on flipscreen
And to stop a pending job from running:

job.cancel();

You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub #26 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AEyN0Lpp5D7KwA60FAIY1j04XcJNhN3Oks5q56U1gaJpZM4KktY1.

@fatcerberus
Copy link
Contributor Author

I don't know that it makes sense to have a Job object before you've actually dispatched something? Until then you don't have a job, just a function.

Also it's awkward: It suggests you can dispatch the same job more than once, but if you do that, what does it mean to do job.cancel()? Does it cancel all instances of the job, only the most recent one? It's cleaner to create the job object on dispatch. Then there's no confusion as to what .cancel() means.

I can rename Dispatch.now() but it seems clear at least to me: you're dispatching the job now, but it's called in the next tick.

@fatcerberus
Copy link
Contributor Author

I guess it would make sense to have an undispatched Job object if you want to pass it around for reuse--but in that case you can just pass the function around since JS has first-class functions? I guess it might make sense for extensibility if there were more types of jobs than just "call this function". An interesting proposition, something worth thinking about at least.

@fatcerberus
Copy link
Contributor Author

One thing I like about this API setup over, say, the Node.js event system is that it's much easier to cancel jobs. In Node, to remove an event listener you have to keep a reference to the callback around so that you can pass it to removeListener() (which can sometimes be awkward since func.bind(...) !== func). With the Dispatch setup as long as you store the token returned by the dispatch call, you can freely pass in closures, lambdas, or bind()'d functions directly. You still have to keep a reference to something, but the code ends up looking cleaner.

@fatcerberus
Copy link
Contributor Author

I also added Dispatch.later(timeout, fn) for time-delayed calls a la setTimeout().

@joskuijpers
Copy link
Contributor

You are right, having undispatched is odd, especially when multiple times dispatching. Nvm me :P

On 02 Nov 2016, at 06:47, Bruce Pascoe [email protected] wrote:

I also added Dispatch.later(timeout, fn) for time-delayed calls a la setTimeout().


You are receiving this because you commented.
Reply to this email directly, view it on GitHub #26 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AEyN0My9s3v-qkV0t0RyBVwLOqB9__W_ks5q6CQEgaJpZM4KktY1.

@fatcerberus
Copy link
Contributor Author

A useful feature I'm thinking of would be for Dispatch.onFlip() to take an optional "priority" argument that determines render order (highest priority = called last, so that it renders "on top"). That would provide most of the benefits of the threads module right in the core, for very little extra complexity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants