emscripten-core / emscripten-core/emscripten
EM_ASYNC_JS didn't end as expected when listen dom event
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
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
```text
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
```cpp
// example.h
#pragma once
#include
#include
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
#include
#include
#include
#include
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("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("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("appendChild", newCloseElem);
documentElem["body"].call("appendChild", newDialogelem);
newCloseElem.call("addEventListener", emscripten::val("click"), emscripten::val::module_property("eventlistener"), false);
emscripten::val dialogElem = documentElem.call("getElementById", std::string("modal-dialog"));
if (false == dialogElem.isNull() || false == dialogElem.isUndefined())
{
dialogElem.call("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();
example* pExample = (example*)uDataContext;
if (pExample)
{
pExample->HandEvent(event);
}
}
void example::HandEvent(emscripten::val event)
{
std::string strEvent = event["type"].as();
if (strEvent == "click")
{
emscripten::val documentElem = emscripten::val::global("document");
emscripten::val dialogElem = documentElem.call("getElementById", std::string("modal-dialog"));
if (false == dialogElem.isNull() || false == dialogElem.isUndefined())
{
dialogElem.call("close");
}
}
event.call("preventDefault");
event.call("stopPropagation");
}
EMSCRIPTEN_BINDINGS(DialogContentContextItemEvent)
{
emscripten::function("eventlistener", &example::OnEvent);
}
//exampleexport.cpp
#include "example.h"
#include
#include
EMSCRIPTEN_BINDINGS(exampleWrapper)
{
emscripten::class_("example")
.constructor<>()
.function("DoModal", &example::DoModal);
}
```
```html
Test Demo
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
var webReportCtrl = undefined;
Module = {};
Module.onRuntimeInitialized = async function () {
webReportCtrl = new Module.example();
webReportCtrl.DoModal();
};
```
After check the output result, I found it will output warning and error in terminal.
The output result as following:
```text
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
```cpp
EM_JS(void, BindFun, (), {
var elem = document.getElementById("modal-dialog-close");
elem.addEventListener("click", ()=> {
var dialogElem = document.getElementById("modal-dialog");
dialogElem.close();
});
});
//newCloseElem.call("addEventListener", emscripten::val("click"), emscripten::val::module_property("eventlistener"), false);
BindFun();
```
The result is right.
The output result as following:
```text
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.
Contributor guide
Assessment
This issue has not been assessed yet.