Emscripten, зажатый асинхронным кодом Javascript - PullRequest
0 голосов
/ 17 ноября 2018

Я пытаюсь использовать 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 и может представить, что это может помочь мне достичь моей цели?

Есть идеи, что я мог сделать?

...