Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: gleng 29. 12. 2021, 23:21:24
-
S Promise a await som v zivote v JS nerobil a ani sa nechystam ale mam tu jednu situaciu kedy ich potrebujem a neviem si rady.
Mam funkciu ktora urobi request na externe api a streamuje z neho data. Potrebujem z vonku predat tejto funkcii promise a ked sa resolvne tak sa vnutri funkcie uzatvori spojenie na to externe api a prestane z neho streamovat data.
nieco ako
function streamData(do, cancel) {
...
do(data) // nech nieco spravi s nastreamovanymi datami
cancel.then(() => api.stop())
}
let closer = new Promise((resolve) => {
setTimeout(resolve, 10000)
});
streamData(console.log, closer)
A ja potrebujem ten promise resolvnut az kedy ja potrebujem skratka, niekde z vonku. Co som cital tak promise zavola hned ten exekutor ktory ma resolve a reject argumenty(mam tu len resolve) tak nejak neviem vymysliet aku funkciu tomu dat aby to cakalo az na moj povel proste.
Taktiez by asi funkcia streamData mala asi vratit promise, alebo sa nejak **** do pozadia, nech neblokuje exekuciu dalsieho kodu. Proste nech si streamuje niekde v pozadi a ja o nej neviem dokial ju nezrusim s tym resolve.
-
V tom, co potřebujete, se nikde žádné Promise nevyskytuje. Prostě jenom z té funkce streamData vrátíte funkci, jejímž zavoláním se streamování zastaví:
const streamData = (do) => {
...
do(data) // nech nieco spravi s nastreamovanymi datami
return () => api.stop()
}
const cancel = streamData(console.log)
setTimeout(cancel, 10000)
Dalo by se to udělat i s tím Promise, jenom si tu proměnnou resolve musíte vytáhnout někam ven. Řešení na prasáka by bylo uložit to do globální proměnné, lepší řešení je zapouzdřit to do funkce a z té vracet Promise i tu ukončovací funkci.
-
Promise se používá na situaci, kdy něco zavoláš co dá výsledek až po nějaké době (třeba až dojdou data po síti) a chceš zavolat callback v okamžiku, kdy ta data jsou ready.
Na tohle je nepotřebuješ, mrkni na stream API: https://nodejs.org/api/stream.html
Prostě otevřeš stream, ten bude na event data ty data zpracovávat. No a až budeš chtít skončit, tak na ten stream zavoláš destroy.
-
V tom, co potřebujete, se nikde žádné Promise nevyskytuje. Prostě jenom z té funkce streamData vrátíte funkci, jejímž zavoláním se streamování zastaví:...
Ten stream je kontinualny a vzdy ked mu pridu data tak ich "posle" to tej do() funkcie. ja na ten stream necakam kym skonci. on bezi stale v pozadi skratka. preto nemozem cakat kym skonci a vrati nieco.
Promise se používá na situaci, kdy něco zavoláš co dá výsledek až po nějaké době (třeba až dojdou data po síti) a chceš zavolat callback v okamžiku, kdy ta data jsou ready.
Precitaj si znovu moju otazku. Ak nieco stale nie je jasne tak doplnim.
-
Precitaj si znovu moju otazku. Ak nieco stale nie je jasne tak doplnim.
To vy chcete poradit. Otázku si není potřeba číst znova, protože to, co píšete v otázce, nedává žádný smysl. Napište, v jakém prostředí se pohybujete (prohlížeč, Node.js, něco jiného), co přesně je ten stream, co přesně potřebujete vyřešit. Pak teprve můžeme poradit, jak to řešit.
Ten stream je kontinualny a vzdy ked mu pridu data tak ich "posle" to tej do() funkcie. ja na ten stream necakam kym skonci. on bezi stale v pozadi skratka. preto nemozem cakat kym skonci a vrati nieco.
Já jsem ale nepsal, že máte čekat na dokončení streamu, ale na dokončení té vaší funkce, která asi zahajuje čtení ze streamu. Vzhledem k tomu, že nevíme, co ten váš kód vlastně dělá, nemůžu k tomu napsat nic víc.
JavaScript se chová, jako by byl jednovláknový (ponechme stranou WebWorkery). Pokud by to vaše čtení ze streamu bylo blokující, nikdy nemůžete zavolat nějaký jiný kód, který by ho ukončil. Ono ale nejspíš blokující není – akorát potřebujeme vědět, co je ten stream zač, pokud vám máme poradit, jak přesně s ním pracovat.
-
Tu je priblizna funkcia:
streamData(req, cancel, ok, nok) {
let query = new URLSearchParams(req);
let events = new EventSource(client.defaults.baseURL + "/api/data?" + query.toString());
events.addEventListener("data", (event) => ok(JSON.parse(event.data)))
events.onerror = (err) => nok(err)
cancel.then(() => events.close())
},
tu je volanie:
let closer = new Promise((resolve) => {
setTimeout(resolve, 10000)
});
streamData(
{id: "foo"},
closer,
(data) => console.log(data),
(err) => console.error(err),
)
a potom ak uzivatel ide na inu stranku alebo chce stream zastavit tlacidlom tak zavolat nieco ako closer.resolve() namiesto toho timeoutu ktory je len na skusku tam.
JavaScript se chová, jako by byl jednovláknový
Ano, viem. Preto sa tymto veciam v JS radsej vyhybam(aj tak som backendak).
-
function Closer() {
let r;
const p = new Promise((resolve, reject) => {
r = resolve
});
p.resolve = r;
return p;
}
const closer = Closer();
-
Tu je priblizna funkcia:
No, konečně se někam dostáváme. Z té funkce streamData()
vrať ty events
, ulož si je někam a až budeš chtít skončit, zavolej na nich events.close()
. Případně pokud bys to chtěl zapouzdřit, tak ze streamData()
vrať funkci, která to skončí:
return () => events.close();
Ale v každém případě je tady použití Promise patternu nesmyslné.
-
Je to přesně jak píše L.., případně jak jsem to psal hned v první odpovědi. Ta varianta s vrácením vlastní funkce (místo celého EventSource) je z hlediska návrhu čistší. Nezveřejňujete implementační detaily, ale vracíte vlastní API – funkci, pomocí které je možné ukončit načítání (a volající nemusí řešit, jak přesně je načítání řešené, prostě má jenom funkci, která načítání ukončí).
Jinak ta funkce streamData je neblokující – zahájí stahování a nastaví posluchače událostí a ukončí se. Takže není problém poslední řádek změnit na return, vrátit z funkce streamData ukončovací funkci a tu někdy později zavolat.
Promise do toho vůbec netahejte, nejsou potřeba a akorát by kód zatemnily.
-
konstrukt, ktery chcete, se jmenuje defer nebo deferred
jak pisi jini, vetsinou je to antipattern
-
function Closer() {
let r;
const p = new Promise((resolve, reject) => {
r = resolve
});
p.resolve = r;
return p;
}
const closer = Closer();
toto som hladal, diky. zatial to funguje, ak to bude robit neskor problemy tak to prerobim. eventuelne mozno pouzijem websocket a budem to riesit uplne inak.
-
function Closer() {
let r;
const p = new Promise((resolve, reject) => {
r = resolve
});
p.resolve = r;
return p;
}
const closer = Closer();
toto som hladal, diky. zatial to funguje, ak to bude robit neskor problemy tak to prerobim. eventuelne mozno pouzijem websocket a budem to riesit uplne inak.
Když odstraníte to zbytečné Promise, dostanete kód, o kterém jsem psal já a L..
-
Když odstraníte to zbytečné Promise, dostanete kód, o kterém jsem psal já a L..
on presne specifikoval, jake chce API.
ja bych asi pouzil tohle https://www.npmjs.com/package/p-defer
-
on presne specifikoval, jake chce API.
Což nic nemění na tom, že je to nesmysl.
ja bych asi pouzil tohle https://www.npmjs.com/package/p-defer
Ano, zejména by bylo dobré se držet té tučné červené věty na začátku: „Don't use this unless you know what you're doing.“
-
Kurna, to se mi ani cist poradne nechtelo, takvy zmateny dotaz. Dam ti tip, ujasni si:
1. Co je to promis
2. Co je to asynchronni volani funkce
3. K cemu je async a await keywords
4. Proc node.js standardni knihovni funkce nevraci promis - protoze jsou stare.
5. Jak si muzes libovolnou starou asynchronni funkci v node.js sam predelat na promis
6. A jak si nad tim pak muzes pouzit async await.
A mas to. A jestl ito chces mit komplet, tak jeste
7. Co je to event loop v node.js a web browseru
-
Když odstraníte to zbytečné Promise, dostanete kód, o kterém jsem psal já a L..
Jojo, proč to napsat jednoduše na jeden řádek, když se to dá napsat složitě na devět...
-
Kurna, to se mi ani cist poradne nechtelo, takvy zmateny dotaz. Dam ti tip, ujasni si:
1. Co je to promis
2. Co je to asynchronni volani funkce
3. K cemu je async a await keywords
4. Proc node.js standardni knihovni funkce nevraci promis - protoze jsou stare.
5. Jak si muzes libovolnou starou asynchronni funkci v node.js sam predelat na promis
6. A jak si nad tim pak muzes pouzit async await.
A mas to. A jestl ito chces mit komplet, tak jeste
7. Co je to event loop v node.js a web browseru
tazatel ma jasno, ale Jirsak a L. neodpovidaji na jeho otazku.
-
Když odstraníte to zbytečné Promise, dostanete kód, o kterém jsem psal já a L..
Jojo, proč to napsat jednoduše na jeden řádek, když se to dá napsat složitě na devět...
je to na jeden radek, kdyz pouzije hotovou synchronizacni primitivu (funkce Closer v me odpovedi), nebo alternativni Promise, treba Bluebird, ktery tohle umi out of box.
-
je to na jeden radek, kdyz pouzije hotovou synchronizacni primitivu (funkce Closer v me odpovedi), nebo alternativni Promise, treba Bluebird, ktery tohle umi out of box.
Jenže moje (naše) řešení problému nepotřebuje žádný extra kód navíc. Prostě jednoduché vrácení funkce, kterou následně zavolá. Čistší, jednodušší, průhlednější, kratší.
-
tazatel ma jasno, ale Jirsak a L. neodpovidaji na jeho otazku.
Ne, tazatel nemá jasno. Já a L.. jsme popsali správné řešení jeho problému (když jsme se konečně dozvěděli, jaký problém řeší). Zato vaše odpovědi jsou zmatené – tazatel se ptá, jak má použít Promise, a vy mu odpovíte, že má použít Defer/Deferred, což je jenom jiný název pro Promise. A použití Promise je v tazatelově případu nesmysl, protože Promise se používá v případech, kdy mám nějaký proces, který běží z pohledu mého kódu asynchronně na pozadí a já potřebuji být informován o jeho dokončení. Tady je to ale opačný případ, tazatel nechci být informován o dokončení, ale naopak chce asynchronní proces ukončit – směr té komunikace je opačný. Promise se tam dá vnutit, jak jsem psal hned v první odpovědi, ale je to matoucí. A matoucí kód je špatně, protože často nepoznáte, jestli v něm je nebo není chyba – a pokud to poznáte teď, nepoznáte to vy nebo někdo jiný později, až ten kód budete upravovat.
-
tazatel ma jasno, ale Jirsak a L. neodpovidaji na jeho otazku.
Ne, tazatel nemá jasno. Já a L.. jsme popsali správné řešení jeho problému (když jsme se konečně dozvěděli, jaký problém řeší). Zato vaše odpovědi jsou zmatené – tazatel se ptá, jak má použít Promise, a vy mu odpovíte, že má použít Defer/Deferred, což je jenom jiný název pro Promise. A použití Promise je v tazatelově případu nesmysl, protože Promise se používá v případech, kdy mám nějaký proces, který běží z pohledu mého kódu asynchronně na pozadí a já potřebuji být informován o jeho dokončení. Tady je to ale opačný případ, tazatel nechci být informován o dokončení, ale naopak chce asynchronní proces ukončit – směr té komunikace je opačný. Promise se tam dá vnutit, jak jsem psal hned v první odpovědi, ale je to matoucí. A matoucí kód je špatně, protože často nepoznáte, jestli v něm je nebo není chyba – a pokud to poznáte teď, nepoznáte to vy nebo někdo jiný později, až ten kód budete upravovat.
Zrovna tak můžeme čtecí proces považovat za poskytovatele dat který chce být informován pokud o jeho data není nadále zájem (= zanikne poslední odběratel dat). Pak o tom chce být informován protože může přestat stream zpracovávat. Tedy je to celé o tom, jak je ten pohled v kontextu dané aplikace postavený - to by měla konceptuálně nastavit právě ta aplikace, aby programátor věděl, jak má na co nahlížet. Jinak ale samozřejmě souhlasím s tím, že Promise je typicky slib budoucích dat nikoli signalizace ukončení jejich odběru. Mimochodem EventBus mi přijde jako vhodný způsob pro obecné informování o změnách stavu různých komponent v aplikaci - ideální pro prototypování, kdy ještě nevíme, jak budou komponenty přesně komunikovat (umožňuje to později rozpoznat specializace a konkretizovat komunikaci podle určení, směru komunikace, trvalosti dat apod.).
-
tazatel se ptá, jak má použít Promise, a vy mu odpovíte, že má použít Defer/Deferred, což je jenom jiný název pro Promise.
neni to stejne https://stackoverflow.com/a/17308358
deferred umoznuje resolvovat a rejectovat manualne z vnejsku, ES6 promisy nemaji deferred interface.
A použití Promise je v tazatelově případu nesmysl, protože Promise se používá v případech, ...
Promise je low level koncept, ktery jde prirozene pouzit v jakemkoliv kodu, ktery na neco ceka (v tazatelove pripade na ukonceni)
-
neni to stejne https://stackoverflow.com/a/17308358
deferred umoznuje resolvovat a rejectovat manualne z vnejsku, ES6 promisy nemaji deferred interface.
Je to stejná implementace. Promise i defer/deferred je obecné označení pro objekt, který umožňuje volat kód „později“, při splnění nějaké podmínky. Případně se to někdy používá pro označení konkrétní části rozhraní – promise je ta část rozhraní, která umožňuje reagovat, defer/deferred je ta část rozhraní, která může aktivovat reakci. Ale implementace je stejná, protože aby to k něčemu bylo, potřebujete vždy obě dvě části – jak spouštěč, tak posluchač.
Váš kód umožňuje resolvovat Promise manuálně z vnějššku, takže to Promise evidentně umožňuje.
Promise je low level koncept, ktery jde prirozene pouzit v jakemkoliv kodu, ktery na neco ceka (v tazatelove pripade na ukonceni)
Ne, v tazatelově případu se nečeká na ukončení. Tazatel chce naopak ukončení ručně vyvolat. Ten směr je opačný – a asi to mate nejen tazetele, ale i vás.
-
Jenže moje (naše) řešení problému nepotřebuje žádný extra kód navíc. Prostě jednoduché vrácení funkce, kterou následně zavolá. Čistší, jednodušší, průhlednější, kratší.
Tvoje "Čistší, jednodušší, průhlednější, kratší." riesenie exportuje internu implementaciu funkcie ktoru caller nema vobec so riesit. Inak povedane, caller len doda funciu na spracovanie dat a "funkciu" na oznamenie ked uz o data nema zuajem. Nic viac. On nema co riesit odkial alebo ako sa data stahuju.
-
Este ma napadol kompromis medzi tym co som chcel ja a co sa tu proklamuje ako najlepsie riesenie:
let closer = function() {}
function close() {
closer()
closer = function() {}
}
function setCloser(c) {
closer = c
}
function streamData(req, setCloser, ok, nok) {
...
setCloser(events.close)
}
...ked nastane cas tak len
close()
-
Tvoje "Čistší, jednodušší, průhlednější, kratší." riesenie exportuje internu implementaciu funkcie ktoru caller nema vobec so riesit.
Tohle mě zajímá; co je u Tebe export interní implementace, snad ne tohle:
return () => events.close();
-
ta funkcia streamData je iba jedna z mnohych funkcii a patri do api kniznice kde sa pracuje s promismi(axios) takze musi mat totoznu logiku ako vsetky ostatne funkcie, ktore nemaju navratovu hodnotu. preto som nemohol/nechcel nic vratit.
ano, hovorit o exportovani internej implementacie je prehnane kedze sme v javascripte, je to zvyk.
return () => events.close by stacilo, ale ako som pisal hore, staci ju zabalit do toho setCloser-u ako anonymnu funkciu.
-
Este ma napadol kompromis medzi tym co som chcel ja a co sa tu proklamuje ako najlepsie riesenie:
let closer = function() {}
function close() {
closer()
closer = function() {}
}
function setCloser(c) {
closer = c
}
function streamData(req, setCloser, ok, nok) {
...
setCloser(events.close)
}
...ked nastane cas tak len
close()
kdyz uz pouzivate globalni promenne, proc neudelate globalni ten eventsource?
-
Váš kód umožňuje resolvovat Promise manuálně z vnějššku, takže to Promise evidentně umožňuje.
protoze jsem pridal metodu resolve a tim jsem z promisu udelal (castecny) deferred.
-
Tvoje "Čistší, jednodušší, průhlednější, kratší." riesenie exportuje internu implementaciu funkcie ktoru caller nema vobec so riesit.
Vy stále nevíte, co ten váš kód vlastně dělá a co má dělat. Caller neexportuje žádnou interní implementaci. Caller exportuje API, ve kterém je funkce, kterou se oznamuje, že už o data nemáte zájem.
Inak povedane, caller len doda funciu na spracovanie dat a "funkciu" na oznamenie ked uz o data nema zuajem.
Tak si tohle rozeberte pořádně. Kdo komu oznamuje (posílá zprávu), že o data nemá zájem? Konzument zpráv a nebo stream? A volání funkce znamená poslání zprávy nebo přijetí zprávy?
ta funkcia streamData je iba jedna z mnohych funkcii a patri do api kniznice kde sa pracuje s promismi(axios) takze musi mat totoznu logiku ako vsetky ostatne funkcie, ktore nemaju navratovu hodnotu. preto som nemohol/nechcel nic vratit.
Tohle jste ale v popisu problému nenapsal. Navíc ta vaše funkce nemá totožnou logiku jako něco jiného, vaše funkce nemá žádnou logiku. To, zda funkce má nebo nemá návratovou hodnotu, není věcí logiky, ale pouze návrhu API.
ano, hovorit o exportovani internej implementacie je prehnane kedze sme v javascripte, je to zvyk.
Ale JavaScript samozřejmě umí omezení platnosti proměnných, nové verze dokonce umí i privátní proměnné a metody.
Na začátku jste psal, že JavaScript není vaše doména. Použít v takovém případě nesmyslné řešení, kterému navíc nerozumíte, když máte k dispozici správné řešení, je dost hloupé. Ale je to váš (marný) boj, když trváte na tom, že to musíte mít špatně, mějte si to špatně. Já jenom doufám, že ten kód nikdy nepotkám, ani jako uživatel.
-
[…] preto som nemohol/nechcel nic vratit.
return () => events.close by stacilo
Pokud se nemá nic vracet, tak se té streamovací funkci může předat kontext (v tomto případě prázdný objekt), funkce v něm nastaví ctx.cancel na tu lambdu, kterou jiní navrhují vrátit, a volající pak zavolá ctx.cancel(). Je to v zásadě to samé, jen se explicitně nic nevrací.
-
[…] preto som nemohol/nechcel nic vratit.
return () => events.close by stacilo
Pokud se nemá nic vracet, tak se té streamovací funkci může předat kontext (v tomto případě prázdný objekt), funkce v něm nastaví ctx.cancel na tu lambdu, kterou jiní navrhují vrátit, a volající pak zavolá ctx.cancel(). Je to v zásadě to samé, jen se explicitně nic nevrací.
Taky dobré řešení. Obecná zkušenost - pokud něco vypadá zbytečně složitě, je dobré zvolenou cestu zrevidovat a zkusit najít jinou. Ale hlavně je dobré (což OP nakonec udělal) osvětlit celý problém vedle žádosti o radu s již rozpečeným řešením.
-
[…] preto som nemohol/nechcel nic vratit.
return () => events.close by stacilo
Pokud se nemá nic vracet, tak se té streamovací funkci může předat kontext (v tomto případě prázdný objekt), funkce v něm nastaví ctx.cancel na tu lambdu, kterou jiní navrhují vrátit, a volající pak zavolá ctx.cancel(). Je to v zásadě to samé, jen se explicitně nic nevrací.
Taky dobré řešení.
Jo, a běžné v kontextu CSP, tady to je jen velmi speciální (podstatně jednodušší) případ.