Я пытаюсь использовать Emscripten для написания Программного обеспечения для работы в браузере, а также на других архитектурах (например, Android, автономное приложение для ПК).
Структура программного обеспечения выглядит примерно так:
main_program_loop() {
if (gui.button_clicked()) {
run_async(some_complex_action, gui.text_field.to_string())
}
if (some_complex_action_has_finished())
{
make_use_of(get_result_from_complex_action());
}
}
some_complex_action(string_argument)
{
some_object = read_local(string_argument);
interm_res = simple_computation(some_object);
other_object = expensive_computation(interm_res);
send_remote(some_object.member_var, other_object);
return other_object.member_var;
}
Давайте назовем main_program_loop
GUI или внешний интерфейс, some_complex_action
промежуточный уровень, а read_local
, send_remode
и expensive_computation
внутренний или нижний уровень.
Теперь внешний и внутренний интерфейсы будут зависеть от архитектуры (например, для Javascript read_local
может использовать IndexDB
, send_remote
может использовать fetch
),
но промежуточный уровень должен составлять более 50% кода (поэтому я не хочу писать его два раза на двух разных языках, а вместо этого написать один раз на C и перенести его в Javascript, для Android я бы использовал JNI ).
Проблемы возникают из-за того, что в Javascript функции на нижнем уровне (fetch
и т. Д.) Выполняются асинхронно (возвращают обещание или требуют обратного вызова).
Один из подходов, которые я попробовал, состоял в том, чтобы использовать обещания и посылать идентификаторы через промежуточный уровень
var promises = {};
var last_id = 0;
handle_click() {
var id = Module.ccall('some_complex_action', 'number', ['string'], [text_field.value]);
promises[id].then((result) => make_us_of(result));
}
recv_remote: function(str) {
promises[last_id] = fetch(get_url(str)).then((response) => response.arrayBuffer());
last_id += 1;
return last_id - 1;
}
Работает для простого случая
some_complex_action(char *str)
{
return recv_remote(str);
}
Но для реальных случаев это кажется действительно сложным, возможно, невозможным. (Я попробовал какой-то подход, в котором я дал каждой функции состояние и каждый раз, когда бэкэнд-функция завершается, функция вызывается и улучшает свое состояние или около того, но код становится чертовски сложным.) Для сравнения, если бы я должен был вызовите some_complex_action
из C или Java, я бы просто вызвал его в потоке, отдельном от потока GUI, и внутри потока все происходило бы синхронно.
Хотелось бы просто вызвать some_complex_action
из асинхронной функции и поместить await
в recv_remote
, но я могу поставить await только непосредственно в асинхронную функцию, а не в какую-либо функцию, вызываемую по линии. Так что эта идея тоже не сработала.
В идеале, если бы я как-то мог остановить выполнение промежуточного транслируемого кода Emscripten до тех пор, пока не завершится бэкэнд-функция, вернитесь из бэкэнд-функции с результатом и продолжите выполнение транслируемого кода.
Кто-нибудь использовал Emterpreter и может представить, что это может помочь мне достичь моей цели?
Есть идеи, что я мог сделать?