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

EM_ASYNC_JS didn't end as expected when listen dom event #23036

Open
Perter-Zhang opened this issue Nov 29, 2024 · 1 comment
Open

EM_ASYNC_JS didn't end as expected when listen dom event #23036

Perter-Zhang opened this issue Nov 29, 2024 · 1 comment

Comments

@Perter-Zhang
Copy link

I am currently simulating a modal dialog.

In CPP code,I will create a H5 element.
There is a close button on the dialog.
and i bind event listeners for close button by embind style.

When click "close" button, the dialog will be close and continue subsequent follow.

To achieve the blocking effect, I used the macro EM_ASYNC_JS to implement it.

In EM_ASYNC_JS Function,
I will create a Promise and listen "close" event for dialog element.
If the "close" event be triggered, The function will return a result.

But when actually executing,
The close event is triggered but it will not be returned to CPP

Version of emscripten/emsdk:
3.1.69

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.25 (febd44b21ecaca86e2cb2a25ef3ed4a0a2076365)
clang version 16.0.0 (https://github.com/llvm/llvm-project effd75bda4b1a9b26e554c1cda3e3b4c72fa0aa8)
Target: wasm32-unknown-emscripten

Full link command and output with -v appended:
emcc example.cpp exampleexport.cpp -o a.js -g -s ASYNCIFY --bind

// example.h
#pragma once
#include <emscripten.h>
#include <emscripten/val.h>

class example
{
private:
public:
  example();
  ~example();

  void DoModal();
  static void OnEvent(emscripten::val event);
  void HandEvent(emscripten::val event);
};
// example.cpp
#include "example.h"
#include <emscripten/html5.h>
#include <emscripten/bind.h>
#include <stdio.h>
#include <iostream>
#include <string>

example::example()
{
}

example::~example()
{
}

EM_ASYNC_JS(int, WaitingClose, (), {
  console.log("waiting close begin");
  var p = new Promise((resolve, reject) => {
    console.log("addEventListener for dialog");
    var dialogElem = document.getElementById("modal-dialog");
    dialogElem.addEventListener("close", ()=> {
      resolve(1);
    });
  });

  let obj = await p;
  console.log("waiting close end");
  return obj;
});


void example::DoModal()
{
  emscripten::val documentElem = emscripten::val::global("document");
  emscripten::val newDialogelem = documentElem.call<emscripten::val>("createElement", std::string("dialog"));
  newDialogelem.set("id", std::string("modal-dialog"));
  newDialogelem["style"].set("width", "600px");
  newDialogelem["style"].set("height", "400px");
  newDialogelem["style"].set("background-color", "#a0a0a0");

  emscripten::val newCloseElem = documentElem.call<emscripten::val>("createElement", std::string("div"));
  newCloseElem.set("id", std::string("modal-dialog-close"));
  newCloseElem.set("data-context", (uintptr_t)this);
  newCloseElem["style"].set("width", "25px");
  newCloseElem["style"].set("height", "25px");
  newCloseElem["style"].set("float", "right");
  newCloseElem["style"].set("background-color", "#ffffff");

  newDialogelem.call<void>("appendChild", newCloseElem);
  documentElem["body"].call<void>("appendChild", newDialogelem);

  newCloseElem.call<void>("addEventListener", emscripten::val("click"), emscripten::val::module_property("eventlistener"), false);

  emscripten::val dialogElem = documentElem.call<emscripten::val>("getElementById", std::string("modal-dialog"));
  if (false == dialogElem.isNull() || false == dialogElem.isUndefined())
  {
    dialogElem.call<void>("showModal");
  }
  
  std::cout << "before" << std::endl;
  int result = WaitingClose();
  std::cout << "close result: " << result << std::endl;
  std::cout << "after" << std::endl;
}

void example::OnEvent(emscripten::val event)
{
  uintptr_t uDataContext = event["target"]["data-context"].as<uintptr_t>();
  example* pExample = (example*)uDataContext;
  if (pExample)
  {
    pExample->HandEvent(event);
  }
}

void example::HandEvent(emscripten::val event)
{
  std::string strEvent = event["type"].as<std::string>();
  if (strEvent == "click")
  {
    emscripten::val documentElem = emscripten::val::global("document");
    emscripten::val dialogElem = documentElem.call<emscripten::val>("getElementById", std::string("modal-dialog"));
    if (false == dialogElem.isNull() || false == dialogElem.isUndefined())
    {
      dialogElem.call<void>("close");
    }
  }

  event.call<void>("preventDefault");
  event.call<void>("stopPropagation");
  
}

EMSCRIPTEN_BINDINGS(DialogContentContextItemEvent)
{
  emscripten::function("eventlistener", &example::OnEvent);
}
//exampleexport.cpp
#include "example.h"
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>

EMSCRIPTEN_BINDINGS(exampleWrapper)
{
    emscripten::class_<example>("example")
        .constructor<>()
        .function("DoModal", &example::DoModal);
}
<!DOCTYPE html>
<html lang="utf-8">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Test Demo</title>
</head>

<body>
</body>
<style>
  html,
  body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
  }
  
</style>
<script>
  var webReportCtrl = undefined;
  Module = {};
  Module.onRuntimeInitialized = async function () {
    webReportCtrl = new Module.example();
    webReportCtrl.DoModal();
  };

</script>
<script src="./a.js"></script>

</html>

After check the output result, I found it will output warning and error in terminal.
The output result as following:

before
waiting close begin
addEventListener for dialog
a.js:941 Aborted(Assertion failed: Cannot have multiple async operations in flight at once)
a.js:964 Uncaught RuntimeError: Aborted(Assertion failed: Cannot have multiple async operations in flight at once)
waiting close end

If I not use embind to bind event listeners for "close" button

EM_JS(void, BindFun, (), {
  var elem = document.getElementById("modal-dialog-close");
  elem.addEventListener("click", ()=> {
      var dialogElem = document.getElementById("modal-dialog");
      dialogElem.close();
  });
});

 //newCloseElem.call<void>("addEventListener", emscripten::val("click"), emscripten::val::module_property("eventlistener"), false);
 BindFun();

The result is right.
The output result as following:

before
waiting close begin
addEventListener for dialog
waiting close end
 close result: 1
after

Does anyone have any idea about this issue?
If I use embind to bind events, how do I adjust my EM_ASYNC_JS function?

I am a beginner in emscripten and may not know much about it.
If there is anything wrong in my description, please forgive me.

@brendandahl
Copy link
Collaborator

I don't see anything immediately wrong with the example, but I do notice your output for emcc -v shows emscripten 3.1.25 which is very old. Maybe try a more recent version/ensure you're actually building with 3.1.69 like you mention.

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