Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   порядок загрузки скриптов и await async (https://javascript.ru/forum/events/85901-poryadok-zagruzki-skriptov-i-await-async.html)

pvv 08.05.2024 21:25

порядок загрузки скриптов и 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() и выполнить всё "синхронно".

Aetae 09.05.2024 03:48

Простой ответ: нет.

Бредовый ответ: наверное можно заернуть какую-то ересь с использованием service-worker: а именно сделать псевдофайл /wait.js, при запросе которого воркер становится остановку и ждёт сообщения о том что wasm загружен, после чего отмирает, и вставить оный сразу после скрипта который должен ждать...

Но это всё такая муть, что куда проще переписать по-нормальному.

pvv 09.05.2024 08:48

Нашел всякие deasync, там да, через вебворкеров дожидаются выполнения асинхронных функций, и да, выглядит очень коряво.

Цитата:

Сообщение от Aetae (Сообщение 555289)
Но это всё такая муть, что куда проще переписать по-нормальному.

А если таких скриптов несколько, да ещё и использующих функции друг друга, то тащить дополнительно везде колбэки и руками добавлять всё в некую очередь чтобы они по цепочке именно друг за другом выполнились, а не как попало, при этом "нормальность" как-то тоже как-то немного тускнеет.

При том что толку от этой асинхронной загрузки всё равно практически никакого, пока все по очереди синхронно не загрузятся делать всё равно особо нечего. Но её зачем-то в некоторых местах (wasm) гвоздями прибили намертво.

Или есть какой-то более простой способ синхронизовать порядок загрузки? через модули?

Aetae 09.05.2024 13:53

Наворотить можно, но на само деле ожидание загрузки прям всех асинхронных скриптов по порядку - это редкая задача.
Обычно в каждом скипте нужно только что-то конкретное.
И тогда просто импортируется только то, что нужно и ждётся в каждом скрипте.

Условно:

/a.js
a = promise

/b.js
b = promise(await a)

/c.js
c = promise(await a)

/d.js
d = promise(await a, await c)

....

pvv 09.05.2024 23:16

Цитата:

Сообщение от Aetae (Сообщение 555296)
И тогда просто импортируется только то, что нужно и ждётся в каждом скрипте.
Условно:
....

да, спасибо.

Только у встроенных скриптов (которым теперь надо ещё будет объявить type="module"), порядок исполнения от этого не поломается? и надо ещё дополнительно теперь возвращать промисы из них где порядок выполнения нужно соблюдать.
Или awaitы резолвятся ровно в том порядке в каком добавлялись? а порядок исполнения для встроенных скриптов вроде должен соблюдаться (хотя насчёт type="module" не уверен)

Aetae 10.05.2024 02:54

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');

}

pvv 10.05.2024 08:55

Огромное спасибо!

pvv 06.06.2024 20:16

С js более менее разобрался, всё работает, ещё раз спасибо Aetae.

Теперь настала очередь непосредственно webassembly, которого также корежит при вызове асинхронных функций.
int main(){
  int x = foo();
  bar(x);
}

Если foo() - асинхронная функция импортированная из JS, то bar будет вызван до того как вернётся foo...

И вот с этим уже официально предлагается бороться через https://web.dev/articles/asyncify, во всяком случае так emscripten вроде бы делает.
Что выглядит уже даже более коряво чем идея заклинить выполнение основного потока каким-нибудь воркером и дожидаясь в его выполнения в каком-нибудь Atomics.wait() "синхронно". По ходу ещё оказалось что sharedarraybuffer позапрещали из-за Spectre.

ткните меня, пожалуйста, в какую-нибудь простую, но рабочую в браузере реализацию чего-то такого:
Цитата:

наверное можно заернуть какую-то ересь с использованием service-worker: а именно сделать псевдофайл /wait.js, при запросе которого воркер становится остановку и ждёт сообщения о том что wasm загружен, после чего отмирает, и вставить оный сразу после скрипта который должен ждать...

Aetae 06.06.2024 20:41

А на кой те вообще в wasm вызывать асинхронные функции? wasm предназначен для оптимизации узких мест. Если у тебя там какая-то асинхронщина - она априори перекроет любые преимущества полученные от wasm на десяток порядков.
Пиши свой асинхронный код на js, а из wasm экспортируй оптимизированные функции для узких мест, а не наоборот.

pvv 06.06.2024 22:11

Ну, например, для реализации dlopen, который зовет Webassembly.instatiate. Вот кто мешал всю эту асинхронщину опциональной сделать, а не гвоздями намертво приколачивать.
или fetch какой-нибудь протащить внутрь wasm.
На кой вообще яваскрипт (ну кроме минимальной необходимой обёртки для запуска wasm), если можно всё собрать в wasm ;)

Aetae 06.06.2024 22:56

Асинхронщина - суть js, на том вся его архитектура стоит. Wasm не замена js, а средство для оптимизации узких мест: подразумевается что работать оно должно аналогично вставкам оптимизированного asm кода при классическом программировании. Если же ты хочешь писать ВСЁ на asm, то, очевидно, придётся страдать на каждом шаге - это твой личный выбор:).

В целом если нет желания писать на js - можно писать на языках, компилирующихся в wasm, наверняка есть множество библиотек с прозрачными биндингами браузерного api, например wasm-bindgen для rust.

voraa 08.06.2024 16:28

Цитата:

Сообщение от Aetae
А на кой те вообще в wasm вызывать асинхронные функции? wasm предназначен для оптимизации узких мест.

Мало, что понимаю в wasm, но вот попалась информация, что там будет ( в Хроме 126) API реализующее Promise
https://v8.dev/blog/jspi-newapi
Цитата:

Сообщение от pvv
На кой вообще яваскрипт (ну кроме минимальной необходимой обёртки для запуска wasm), если можно всё собрать в wasm

Js в браузере решает 99% задач.

Aetae 08.06.2024 19:31

voraa, кнешно будет, запрос же есть на него.
Гугл же сейчас вообще не стесняются пихать в веб стандарты 100500 мильёнов апи и фич на каждый чих и хотелку. Кто-то может сказать что это активное развитие, кто-то что это закрепление монополии, путём физической невозможности реализации новых браузеров.:)

voraa 08.06.2024 20:18

Цитата:

Сообщение от Aetae
путём физической невозможности реализации новых браузеров.

Почему? Большинство АПИ все таки делается с какой то договоренностью с другими разработчиками и потом вносится в стандарт. А потом и Файрфокс и Сафари тоже его начинают его поддерживать. И наверно не просто так делают, а именно потому, что есть запрос.
Ну а если уж какой совсем новый, то пусть будет любезен тоже реализовывать все. Иначе кому такой браузер нужен будет.

Aetae 08.06.2024 20:33

voraa, дык суть как раз в том что оно так (неоправдано) разрасталось, что новый сделать абсолютно нереально. Только развивать то что уже есть. Даже если ты корпорация уровня Майков - проще сдаться:).

voraa 09.06.2024 08:09

Цитата:

Сообщение от Aetae
что новый сделать абсолютно нереально.

Ну так может это и хорошо. Не нужны ни кому куча не совместимых движков. Разработчикам не нужно это в первую очередь. А самих браузеров может быть много. Едж и Хром ведь различаются, у каждого свои плюшки. А движки пусть будут одинаковые (строго поддерживающие стандарт)

pvv 17.06.2024 16:22

Цитата:

Сообщение от Aetae (Сообщение 555433)
Асинхронщина - суть js, на том вся его архитектура стоит.

Только вот wasm про это к сожалению не знает.

Цитата:

Сообщение от Aetae (Сообщение 555433)
Wasm не замена js, а средство для оптимизации узких мест

Это ещё возможность взять готовый кусок кода, собрать в wasm, засунуть в браузер и запустить на любом устройстве не переписывая всё на js, без относительно того, насколько быстро и оптимально это будет работать в результате. Кроссплатфомернность которую мы заслужили...

Цитата:

Сообщение от Aetae (Сообщение 555433)
В целом если нет желания писать на js - можно писать на языках, компилирующихся в wasm, наверняка есть множество библиотек с прозрачными биндингами браузерного api, например wasm-bindgen для rust.

Ну вот там внутри будут ровно те же костыли только ещё и с дополнительной кучей всякого не особо нужного обвеса.
Небольшой кусок кода на С собранный без emscripten и его стандартной библиотеки со всякими почти пустыми затычками для всяких stdio, голым clang получается на 100-150кБ меньше, т.е. раза в два. Засада приключилась с dlopen, и загрузкой функций из других wasm модулей, так как к wasm.instantiate гвоздями зачем-то приколотили async. При том что есть и синхронный вызов, но с ограничениями по размеру. Кто это интересно, #$%^!, решил что 4кБ будет достаточно для всех, а если там вдруг 10кБ то пользователь прям помрёт от лишней 1мс синхронного ожидания пока wasm это загрузит.

Цитата:

Сообщение от voraa (Сообщение 555439)
Мало, что понимаю в wasm, но вот попалась информация, что там будет ( в Хроме 126) API реализующее Promise
https://v8.dev/blog/jspi-newapi

да, когда-нибудь будет, а потом возможно даже и не только в хроме, "жаль только жить в эту пору прекрасную..."


https://github.com/dsyer/async-wasm
я могу также руками добавить callback в wasm, в который потом верну результат асинхронной функции.
я не до конца понимаю следующее:
вызов идёт из wasm, и как мне его затормозить и подождать выполнения асинхронной функции в JS, пусть даже и в тупом цикле while(1), но чтобы при этом периодически "отдавать управление" обратно JS для выполнения его стэка вызовов с асинхронными функциями.

p.s. Для костылей "деасинхронизации" с воркерами, SharedArrayBuffer необходимый для Atomics.wait, забанили из-за spectre и включается обратно этот CORS в заголовках http от сервера.
Если я html файл открываю браузером с диска, без сервера, включить в нём sharedarraybuffer во встроенных <script> возможности я так понимаю нет? (без всяких --enable-features=SharedArrayBuffer при запуске браузера)

Aetae 18.06.2024 06:12

pvv, об "открытии html файла с диска" вообще забудь. Фактически это не поддерживаемая на данный момент технология. Проще перечислить что там будет работать, чем то что не будет.


Часовой пояс GMT +3, время: 10:53.