DEV Community

HarmonyOS
HarmonyOS

Posted on

How to obtain the return value after executing an asynchronous method with runJavaScript ?

Read the original article:How to obtain the return value after executing an asynchronous method with runJavaScript ?

How to obtain the return value after executing an asynchronous method with runJavaScript ?

Problem Description

The official documentation mentions that the runJavaScript method cannot obtain the return value when executing asynchronous methods. How can this issue be resolved?

img

Background Knowledge

In applications, the use of JavaScript in front-end pages is primarily divided into: the application side calling front-end page functions, and the front-end page calling application-side function methods to invoke JavaScript-related functions on the front-end page.

  • Application-side invocation of front-end page functions: The application side can call JavaScript-related functions on the front-end page through the runJavaScript() and runJavaScriptExt() methods.
  • Front-end page calls application-side functions: There are two ways to register application-side code. One is to call it during the initialization of the Web component using the javaScriptProxy() interface. The other is to call it after the Web component has been initialized, using the registerJavaScriptProxy() interface. Both methods need to be used in conjunction with the deleteJavaScriptRegister interface to prevent memory leaks.

Troubleshooting Process

  1. Problem Analysis
    The challenge is to execute an asynchronous JavaScript method in a webview and retrieve its return value using runJavaScript(), which itself is asynchronous.

  2. Key Points Verification

    • Asynchronous Limitation: runJavaScript() is asynchronous, so directly capturing its return value is not possible.
    • Workaround Approach:
      • Define a synchronous wrapper function in the frontend to call the asynchronous method.
      • Use a callback mechanism to pass the result back to the app side.
  3. Potential Issues

    • Memory Leaks: Improper use of deleteJavaScriptRegister could lead to memory leaks.
    • Lifecycle Management: Ensure the JavaScriptProxy is properly registered and unregistered during app lifecycle events.

Solution

The reason why runJavaScript cannot obtain the return value when executing asynchronous methods is as follows:
runJavaScript itself is executed asynchronously, which means that runJavaScript is essentially an asynchronous method. Currently, there is no solution to obtain the return value when calling another asynchronous method.
We can only approach this issue from other angles.

  • Define a synchronous method in the front-end code and execute it using runJavaScript.
  • Within the front-end synchronous method, call the actual front-end asynchronous method that you intend to execute.
  • After obtaining the return value from the front-end asynchronous method, the front-end invokes an application-side method to pass the return value as a parameter.

This resolves the issue of not being able to obtain return values when executing asynchronous methods with runJavaScript.

  • indexByTest.html.
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>title</title>
</head>
<body>
<p>Front-end page</p>
</body>
<script>
    const testAsync = (a, b, c) => {
       return new Promise((resolve) => {
           setTimeout(() => {
              resolve(`Asynchronous return${a},${b},${c}`)
           }, 3000)
       })
    }
    function testSync(a, b, c) {
      testAsync(a, b, c).then(res => {
        testObjName.testSyncCallback(res)
      })
    }
</script>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Index.ets.
import { webview } from '@kit.ArkWeb';

class TestObj {
  obj?: Index

  constructor(obj: Index) {
    this.obj = obj
  }
  testSyncCallback(res: string) {
    console.info('testSyncCallback: ', res)
    if(this.obj) {
      this.obj.msg = res
    }
  }
}

@Entry
@Component
struct Index {
  private webviewController: WebviewController = new webview.WebviewController()
  private localPath = $rawfile('indexByTest.html')
  private testObj = new TestObj(this)
  @State msg: string = ""

  aboutToAppear(): void {
    // You need to apply for the ohos.permission.INTERNET permission.
    webview.WebviewController.setWebDebuggingAccess(true);
  }

  aboutToDisappear(): void {
    this.webviewController.deleteJavaScriptRegister('testObjName')
  }

  build() {
    Column({space: 6}) {
      Web({controller: this.webviewController, src: this.localPath})
        .javaScriptAccess(true)
        .javaScriptProxy({object: this.testObj, name: 'testObjName', methodList: ['testSyncCallback'],
          controller: this.webviewController})
        .width('60%')
        .height('60%')
      Text(this.msg ? "The data returned by JS: " + this.msg : "")
        .fontSize(12)

      Button('Return value of an async func')
        .fontSize(8)
        .height(30)
        .width('85%')
        .onClick(() => {
          this.webviewController.runJavaScript('testSync(1, 2, "666")');
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}
Enter fullscreen mode Exit fullscreen mode

Verification Result

output6.png

Written by Emrecan Karakas

Top comments (0)