порядок загрузки скриптов и await async
отдельные скрипты должны выполняться синхронно по очереди
<html><body> <script src = 'script.js'></script> <script>console.log("internal script", result)</script> </body>/<html> но в script.js async function func(){instance = await WebAssembly.instantiate ...} document.currentScript.onload = async function(){ result = await func() console.log("onload complete") } наличие асинхронной функции слегка ломает порядок загрузки скриптов так как встретив await начнётся загрузка следующего скрипта не дождавшись выполнения предыдующего. Можно ли как-то сломать здесь асинхронный вызов func() чтобы дождаться загрузки и выполнения первого скрипта и только потом продолжить разбирать дальше html и выполнять остальные скрипты по очереди? Я более менее понимаю как это надо делать правильно, но хочется понять как это сделать неправильно: не меняя все остальные скрипты, проверяя результат промисов или запихивая в колбэки. А просто затупить до выполнения func() и выполнить всё "синхронно". |
Простой ответ: нет.
Бредовый ответ: наверное можно заернуть какую-то ересь с использованием service-worker: а именно сделать псевдофайл /wait.js, при запросе которого воркер становится остановку и ждёт сообщения о том что wasm загружен, после чего отмирает, и вставить оный сразу после скрипта который должен ждать... Но это всё такая муть, что куда проще переписать по-нормальному. |
Нашел всякие deasync, там да, через вебворкеров дожидаются выполнения асинхронных функций, и да, выглядит очень коряво.
Цитата:
При том что толку от этой асинхронной загрузки всё равно практически никакого, пока все по очереди синхронно не загрузятся делать всё равно особо нечего. Но её зачем-то в некоторых местах (wasm) гвоздями прибили намертво. Или есть какой-то более простой способ синхронизовать порядок загрузки? через модули? |
Наворотить можно, но на само деле ожидание загрузки прям всех асинхронных скриптов по порядку - это редкая задача.
Обычно в каждом скипте нужно только что-то конкретное. И тогда просто импортируется только то, что нужно и ждётся в каждом скрипте. Условно: /a.js a = promise /b.js b = promise(await a) /c.js c = promise(await a) /d.js d = promise(await a, await c) .... |
Цитата:
Только у встроенных скриптов (которым теперь надо ещё будет объявить type="module"), порядок исполнения от этого не поломается? и надо ещё дополнительно теперь возвращать промисы из них где порядок выполнения нужно соблюдать. Или awaitы резолвятся ровно в том порядке в каком добавлялись? а порядок исполнения для встроенных скриптов вроде должен соблюдаться (хотя насчёт type="module" не уверен) |
pvv, не особо разобрал твой вопрос но:
Обычные скритпы которые ни от чего не зависят и от которых ничего не зависит - не трогать. Обычные скрипты от которых кто-то зависит - ставить вверх в нужном порядке. Скрипты с чем-то асинхронным которые ни от чего не зависят и от которых ничего не зависит - не трогать. Скрипты с чем-то асинхронным от которых кто-то зависит - возвращать в конце промис ждущий исполнения и имеющий конкретное имя(от которого и будут зависеть последующие). Промис возвращать либо как глобальную переменную в обычном скрипте, либо как экспорт в модуле. Скрипты с чем-то асинхронным которые от чего-то зависят - await'ить зависимости по имени перед исполнением. Скрипты с чем-то асинхронным которые от чего-то зависят И от которых кто-то зависит - await'ить зависимости по имени перед исполнением И возвращать в конце промис ждущий исполнения и имеющий конкретное имя(от которого и будут зависеть последующие). Промис возвращать либо как глобальную переменную в обычном скрипте, либо как экспорт в модуле. Но чтоб совсем не париться - вот тебе на коленке накидал систему работы с асинхронными зависимостями не зависящую от порядка: { // top-script.js this.dependencies = { table: {}, get(name) { if (Array.isArray(name)) return Promise.all(name.map(n => this.get(n))); return this.table[name] ??= dependenciesProxyPromise(); }, set(name, truePromise) { const existingPromise = this.table[name]; if (existingPromise) { const attach = existingPromise[dependenciesProxyPromise.attach]; if (!attach) throw new Error(`[duplicate dependency declaration: ${name}]`); attach(truePromise); } else { this.table[name] = truePromise; } return this; }, }; function dependenciesProxyPromise() { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); promise[dependenciesProxyPromise.attach] = (truePromise) => { truePromise.then(resolve, reject); delete promise[dependenciesProxyPromise.attach]; return promise; };; return promise; } dependenciesProxyPromise.attach = Symbol('attach'); // просто для удобства const delay = (ms) => new Promise(r => setTimeout(r, ms));; // first.js console.log('first file start'); // ... const first = async () => { console.log('first proxy start'); await delay(3000); console.log('first proxy end'); }; dependencies.set('first', first()); console.log('first file end') // second.js console.log('second file start'); async function second() { console.log('second proxy start'); const third = await dependencies.get('third'); // ... await delay(1000); console.log('second proxy end'); } dependencies.set('second', second()); console.log('second file end'); // main.js console.log('main file start'); async function main() { const [ first, second, third ] = await dependencies.get(['first', 'second', 'third']); // ... console.log('main file after all dependencies end'); } main() console.log('main file end'); // third.js console.log('third file start'); const third = async () => { console.log('third proxy start'); await delay(3000); console.log('third proxy end'); } // ... dependencies.set('third', third()); console.log('third file end'); } |
Огромное спасибо!
|
С js более менее разобрался, всё работает, ещё раз спасибо Aetae.
Теперь настала очередь непосредственно webassembly, которого также корежит при вызове асинхронных функций. int main(){ int x = foo(); bar(x); } Если foo() - асинхронная функция импортированная из JS, то bar будет вызван до того как вернётся foo... И вот с этим уже официально предлагается бороться через https://web.dev/articles/asyncify, во всяком случае так emscripten вроде бы делает. Что выглядит уже даже более коряво чем идея заклинить выполнение основного потока каким-нибудь воркером и дожидаясь в его выполнения в каком-нибудь Atomics.wait() "синхронно". По ходу ещё оказалось что sharedarraybuffer позапрещали из-за Spectre. ткните меня, пожалуйста, в какую-нибудь простую, но рабочую в браузере реализацию чего-то такого: Цитата:
|
А на кой те вообще в wasm вызывать асинхронные функции? wasm предназначен для оптимизации узких мест. Если у тебя там какая-то асинхронщина - она априори перекроет любые преимущества полученные от wasm на десяток порядков.
Пиши свой асинхронный код на js, а из wasm экспортируй оптимизированные функции для узких мест, а не наоборот. |
Ну, например, для реализации dlopen, который зовет Webassembly.instatiate. Вот кто мешал всю эту асинхронщину опциональной сделать, а не гвоздями намертво приколачивать.
или fetch какой-нибудь протащить внутрь wasm. На кой вообще яваскрипт (ну кроме минимальной необходимой обёртки для запуска wasm), если можно всё собрать в wasm ;) |
Часовой пояс GMT +3, время: 22:09. |