Closed ZhenFTW closed 2 years ago
I haven't thought about this, but this is a valid concern. I suppose something like evaluate_js_async can be implemented using js api. Thoughts on architecture and API are appreciated.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Figured I would comment here to let it be known that I to also am in possession of a great desire to have a feature of similar functionality to that which has been implicated therein.
I have drafted an implementation in the evaluate_js_async
branch. See examples\evaluate_js_async.py
for usage details.
Tested on macOS, but not elsewhere.
I cant find any file named evaluate_js_async.py And looking at that branch, it does not seem like you have made any recent commits to the examples folder, only the webview folder.
My bad. The async example is pushed now.
It's very educational looking through your implementation. What about Asyncio? It's nice to be able to wait for the JS to return something, but it's less useful if we can only wait for one thing at a time. Maybe window.evaluate_js_async that returns an awaitable. And similarly the ability to expose python coroutines.
My async python knowledge is limited, so suggestions are welcomed.
Well this is my first real project using it, but I think that before you call a python API function you need to use the inspect library to check if the function is a coroutine, and run it with the await keyword if it is. or maybe via asyncio.run_coroutine_threadsafe(coro_func(), loop) if you want to allow the user to pass their own event loop.
loop.call_soon_threadsafe(callback, *args, context=None) is also probably important.
Here is kind of the simplest API I can think of that I would expect to work.
class Api: def __init__(self): self.i = 0 async def IOBoundFunction(self): self.i += 1 await asyncio.sleep(random.randint(1, 30)) return self.i
So I could hammer the button that calls "IOBoundFunction" over an over really fast and none of the separate calls would block each other.
Also when I call pywebview.api.IOBoundFunction() I would like a way to cancel the coroutine.
So I looked into it over the weekend and I think the simplest way to add what I'm looking for is to add an event_loop optional parameter to the window object & three lines to the _call function inside js_bridge_call
def js_bridge_call(window, func_name, param, value_id): async def _call(): try: result = func(*func_params.values()) if inspect.iscoroutinefunction(result): future = asyncio.run_coroutine_threadsafe(result, window.event_loop) result = future.result() result = json.dumps(result).replace('\', '\\').replace('\'', '\\'') code = 'window.pywebview._returnValues["{0}"]["{1}"] = {{value: \'{2}\'}}'.format(func_name, value_id, result)
Plus error checking to yell if we don't actually have an event loop. And it would also be nice to have a way to call future.cancel() from the javascript, but I'm not sure how that would work on the JS end since JS promises can't be canceled.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@CiberNin This is quite a dramatic change. While I am open to adding async support, it requires throughout planning, on how to integrate it into pywebview properly. One challenge is pywebview threading and how it would interact with asyncio.
Anyhow I have created a PR for my implementation. Comments are welcomed https://github.com/r0x0r/pywebview/pull/843
If you use run coroutine thread safe then it should just run the passed async function on the same thread as the passed event loop. So if you allow the user to pass an event loop when constructing a window, it gives them controll over the context in which the async function is called. Future.result will block the calling thread until the async function is complete. Unless Im missing something I think it drops in pretty cleanly.
I released async evaluate_js in 3.6
asyncio support will be considered for future releases. Somebody can maybe post an example that would illustrate benefits of asyncio in pywebview.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
The message to post on the issue when closing it. If none provided, will not comment when closing an issue.
Somebody can maybe post an example that would illustrate benefits of asyncio in pywebview.
May be it's not an example, but a use case: multiple small services written as async coroutines to be run in one physical core / thread / process. such as web server, db, task manager, reverse proxy, etc. with web GIU. In this architecture having pywebview UI as a main entrypoint is not cool, and attaching async coros pool as a single server to it not ok.
Specification
pywebview version: 3.7 operating system: Window web renderer: edgechromium, edgehtml, cef,
Description
Is there a way to run async calls and js promises on evaluate js? fetch and axios returns none or empty {}. I know that I can just use python request and connect it with js api, but running async js and promises (not only about fetch and axios) in scrape is important in my case. It seems evaluate js can only run synchronous calls. Do it need to communicate to js api to do this like using timeout? Hope you add another api just like on selenium (execute_async_script) that can run promises async api. It would help scraping alot and give more option on evaluating js on windows.