Ono to možná dnes už bude důležitější pro backend než pro frontend. Na frontendu zneužitím takové chyby ovlivníte jednoho uživatele a nejspíš jen weby, které má otevřené. Na serveru (Deno, Node, Cloudflare Workers) tím ale útočník může získat přístup k datům jiných uživatelů.
Pokud jsem autor webové stránky nebo backendu, v obou případech používám knihovny stažené z internetu. V prohlížeči běží v jedné záložce moje aplikace a v druhé záložce třeba aplikace útočníka, nicméně tyhle aplikace běží každá ve svém procesu, pokud vím se svým V8.
Pokud si někde spustíte sám Deno/Node, běží tam jen váš kód. Ale nejsem si jist, jestli třeba Cloudflare Wokers nebo AWS Lambda běží kód jednoho uživatele v samostatném procesu, zda náhodou v jednom procesu neběží kód více uživatelů.
Ladis: Takhle to vypadá, když někdo píše o něčem, čemu nerozumí.
JavaScriptový kód běží vždy v jednom vlákně (nebo to musí být nerozeznatelné od běhu v jednom vlákně), ale kód okolo (JavaScript engine a implementace dodávané skrze něj třeba z prohlížeče) mohou běžet v kolika vláknech chtějí. (Řeč je o hlavním vlákně, ne o Workerech.)
then() není žádná speciální konstrukce a není to žádný syntaktický cukr. then() je normální volání metody – a klidně si sám můžete v JavaScriptu naimplementovat něco, co bude implementovat Promise.
Popletl jste si to s await, což je syntaktický cukr pro then(). await jsem ale záměrně nepoužil, protože pak by ten kód běžel seřazený za sebou a vždy by nejprve došlo k zápisu do bufferu a pak až k jeho uvolnění.
Tak, jak jsem to napsal, ale ty dva fetch() běží z hlediska JavaScriptového kódu paralelně vedle sebe (je jedno, zda v jednom vlákně a střídají se nebo ve více vláknech – to záleží na implementaci fetch()). fetch() totiž neběží v JavaScriptu, běží mimo něj a JavaScript jen čeká na jeho dokončení (to je právě význam Promise). Když se kód vrátí zpět do JavaScriptu, zase se způsobně seřadí za sebe a běží nejprve jeden kód v then() a pak druhý – ale nevíte, v jakém pořadí.
Jen pro úplnost dodávám, že běh toho kódu v then() se nikdy nebude střídat. Spustí se obvykle ten kód od fetch e, který doběhl jako první, ale pak ten kód doběhne celý až do konce, řízení se vrátí zpět do javascriptového enginu a ten teprve po té spustí kód z toho druhého then() (pokud fetch mezi tím doběhl). Tím právě JavaScriptový engine zajistí, že JavaScriptový kód běží vždy jen v jednom vlákně.
Kdyby to takhle nefungovalo, nebyl byste schopen zavolat paralelně vedle sebe např. dva fetch(), které jsou nezávislé a můžou běžet vedle sebe – musel byste zavolat jeden, počkat na výsledek a pak zavolat druhý. Což by zbytečně zdržovalo.
Kdyby to takhle nefungovalo, tak jsou k ničemu metody jako Promise.any().
Takhle to vypadá, když někdo píše o něčem, čemu nerozumí.
Ano, ty fetche v moderních prohlížečích jedou paralelně. Ale jak sám píšeš, vzhledem ke kódu to musí být nerozeznatelné od běhu v jednom vlákně. Tj. reakce na stažený obsah se seřadí do fronty. "buffer.write()" a "buffer.destroy()" nikdy nepojedou současně (tzv. race condition).
EDIT: Bonus vysvětlivka: Race condition není o tom, že když si hodím kostkou, tak podle čísla provedu kód pro číslo 1, 2, 3 atd. Ale o tom, že kód pro dvě čísla se provede současně.
EDIT 2: V tom vašem odkazu na Premise vidíte, že váš kód máte zřetězit:
.then(...)
.then(...)
8. 4. 2024, 10:38 editováno autorem komentáře
Já jsem ale nikdy nepsal, že operace s bufferem pojedou současně. Spustí se po sobě, ale dopředu nevíte, zda to bude v pořadí buffer.write() a pak buffer.destroy(), nebo jestli to bude nejprve buffer.destroy() a pak až buffer.write(). Což může znamenat přístup do nealokované paměti.
K race condition klidně může dojít i na jednom procesoru s jedním vláknem. Race condition totiž nezávisí na tom, že něco probíhá fyzicky ve stejný okamžik, tj. v jednom jádru procesoru se provádí jedna instrukce a v tom samém okamžiku se v jiném jádru procesoru provádí jiná instrukce. Race condition znamená, že není dopředu jasné, v jakém pořadí se provádění kódu dokončí. Což je právě příklad Promise. Nepředstavujte si ten race jenom jako formule na okruhu, kde jedou dvě formule vedle sebe. race zrovna tak může být Člověče nezlob se, kde v jednu chvíli postupuje vpřed pouze jedna figurka, hráči se střídají – takže také není jasné, kdo bude v cíli první.
Ne, já ten kód fakt nechci zřetězit, protože nechci se začátkem druhého HTTP požadavku čekat, až se dokončí první. Představte si to třeba jako stažení dvou velkých souborů, kdy je rychlost omezená na straně serveru (třeba protože server ten soubor teprve za běhu generuje). Když se každý ze souborů bude stahovat deset minut, je rozdíl, jestli zahájíte stahování obou souborů najednou a za 10 minut budete mít stažené oba soubory (jak by to dělal můj kód). Nebo jestli zahájíte stahování jednoho souboru, a až se za 10 minut dokončí, zahájíte stahování druhého souboru, takže je dohromady budete stahovat dvacet minut (jak doporučujete vy).
Podle Wikipedie máte pravdu. Kód samotný může běžet sekvenčně, stačí, když rozhodnutí, jaký kód bude spuštěn, závisí na vnějším náhodném vlivu.
Z mého pohledu je kód v "then" ok, protože ty skutečně nemohou běžet současně. Chybně je kód předtím, který "vybere" náhodně, která kombinace příkazů se provede.
Příklad se stahováním souborů je trochu nešťastný, protože pak by ten příklad musel být, že u jednoho z těch souborů následuje po dokončení smazání druhého. Tj. někdy mi na disku po spuštění stahování obou a počkání do konce zůstane jeden soubor, někdy dva.
Ten příklad ukazoval jenom to, že můžu mít dva Promise, které pracují se stejným objektem. Šlo o to ukázat, že i v jednovláknovém JavaScriptu může dojít k race conditions. Jinak ten kód sám o sobě samozřejmě smysl nedává – pokud by se ten objekt buffer nechal zbořit tím, že na něm zavolám nejdříve destroy() a pak write(), zbořil by ho i následující primitivní kód:
buffer.destroy(); buffer.write();
A to opravdu není něco, čím by se měl nechat objekt uvnitř JavaScriptového enginu rozhodit.
Primárně ale ten sandbox míří na interní implementace uvnitř JavaScriptového enginu, protože těch objektů, které vystavují JavaScriptové API a které se musí vypořádat s paralelním během kde čeho, je v dnešních prohlížečích bambilion.