Closed RickCarlino closed 2 years ago
@RickCarlino Thanks a lot for taking the time to write this up.
Async bindings crossed my mind as well. I haven't thought things through yet, but here are my initial thoughts:
You can't just suspend WebAssembly, so there's no native support for promises or async. There are some discussions of coroutines/stack switching (e.g. here and here) that could help here, but nothing on the horizon AFAICT.
One solution would be to start your async operation from the binding, and when it finishes, you use push()
on the WAForth class to push stuff on the stack, and then interpret()
a callback word you defined. Here's a short example to illustrate. As with all async stuff (at least before the introduction of async/await
), it makes it more tedious to write code this way instead of just using a blocking-style API.
Something I want to try is run WAForth from a worker thread, and use Atomics.wait to block and resume the thread ( I already had this in #43 , but it makes even more sense now with the JS bindings). I think this would allow you to create a blocking API. I have it on my TODO list to try this, and create an example if this approach works out.
One thing that's missing to create useful examples is an API to easily push strings back into WAForth (maybe put them in the PAD area?)
Thanks for the detailed response, @remko that makes sense. In my case, I ended up bringing in DEFER / IS from the 20xx standard.
: DEFER! >BODY ! ;
: DEFER CREATE ['] ABORT , DOES> @ EXECUTE ;
: IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE
As far as string support goes, I think putting them in the PAD area would make sense.
Please let me know if you need any help!
@RickCarlino Good tip!
I added a bindAsync
to the wrapper that makes registering callbacks easier (and also added a pushString
).
I updated the fetch example to illustrate.
This is the core of the code:
forth.bindAsync("ip?", async () => {
const result = await (await fetch("https://api.ipify.org?format=json")).json();
forth.pushString(result.ip);
});
And then in Forth:
: IP?-CB ( true c-addr n | false -- )
IF
." Your IP address is " TYPE CR
ELSE
." Unable to fetch IP address" CR
THEN
;
: IP? ( -- )
['] IP?-CB
S" ip?" SCALL
;
@remko Thanks so much- will give it a look this evening.
Currently, to add a binding to WAForth we use
Forth#bind
:This is good for 90% of cases out there, but I was curious how a developer might handle asynchronous binding usecases. Eg:
Using asynchronous / promised-based JS APIs.
CASE 1: Inbound Async I/O
For dealing with inbound events (such as reacting to DOM
click
events, HTML5 geolocation events, etc..), I think it would be really easy to write an event handler:CASE 2: Outbound Async I/O
For outbound I/O operations like AJAX, things might not be so simple:
Ideas
💡 Allow
bind
to return a promise and block the Forth VM until the promise resolves.Would something like this be reasonably possible with the current system?