Issue
Evaluating Javascript within a Webview is inherently a (possibly) long-running task. On Windows we evalJS accepts a second argument which is a callback function to receive the result (when it's ready). We should support this async variant of the Ti.UI.WebView.evalJS method on Android and iOS (especially now that there's an async method to do exactly this natively in Android as of apilevel-19).
// Existing sync call example
// Result is returned sync
var result = webview.evalJS('1 + 3');
// Async variant
// this variant doesn't return a a value (basically it returns null or undefined but callers shouldn't use the result)
// It fires off a thread/whatever to eval the JS and call the callback async.
webview.evalJS('1 + 3', function (result) {
// do something with result
});
The big issue on iOS is that the UIWebView currently used in Ti.UI.WebView does not offer an API to both do the evalJS call async AND receive back a value. It can either be blocked on the main thread (sync) and return a value or do it async but do not return a value. Different to that, the "more modern" WKWebView that slowly replaces Ti.UI.WebView does it different and offers the API asynchronous by default. I've made [a module](https://github.com/appcelerator-modules/Ti.WKWebView) for that one a while ago, so devs can use it already. [~cwilliams] Let me know your thoughts!
Here's an idea... How about we create an async evalJS() equivalent which does *+not+* support returning a value. It merely executes the given script within the WebView. Developers already have a means to communicate back within the HTML via
Ti.App.fireEvent()
. They can use that to async communicate with the Titanium JavaScript. Sometimes keeping it simple is better.I think because I didn't add the example API we may be getting confused here. An async variant of the method would not return a valid result. It should return null/undefined. Instead it schedules the eval to be run in the Webview and invokes the callback with the result when it's done. Right now the Android version I set up calls the callback function with the result, but doesn't really have a way to handle errors. We could consider a better version which would be more node-like so the first arg is an optional error object, the second argument is the result.
I don't see why iOS wouldn't be able to do this. This is not some magical time-traveling variant that runs async but also returns a value sync.
Hold on. We can already do async eval on all platforms today. We support
Ti.App.addEventListener()
andTi.App.fireEvent()
on the HTML side. So, developera already have the ability to do async event driven communication between the WebView's JavaScript and Titanium's JavaScript. And they can do an evalJS() equivalent by passing the JavaScript string via an event to the WebView like the below...The above works on both Android and iOS. _*Edit:* The above mentioned solution will only work for HTML under the developer's control. It won't work with random webpages loaded from the Internet since they won't have any Titanium event listeners coded into the HTML's JavaScript. So, Chris' async evalJS() still has merit._
https://github.com/appcelerator/titanium_mobile/pull/9889 Also needs a thumb-up from either [~gmathews] or [~jquick]
*Closing ticket* Verified improvement in SDK Version
7.5.0.v20181016071050
using elements from the example in the description. *Test environment*