Permalänk
Medlem

Vänta på forEach (Javascript)

Hej!

Jag försöker greppa mig kring detta.

Jag försöker uppdatera en array med ny data.

Men den lyckas uppdatera localStorage med "fel array". Jag vill att den går igenom en forEach loop först och sedan när den är klar skall den lägga den uppdaterade arrayn som localstorage.

Hur använder man t.ex. Promises för med en forEach eller någon annan metod att vänta på min forEach att bli klar.

Exempel på hur detta kan se ut.

updateSomething(){ var newArr = JSON.parse(localStorage.getItem('theData')) newArr.forEach((element)=>{ return this.updateData(element); //funktion som uppdaterar datan och return elementet }) console.log(newArr) //Detta visar korrekt data localStorage.setItem('theData', JSON.parse(newArr)); // detta visar gamla datan }

Visa signatur

Gaemer Addicted!

Permalänk
Medlem

Det är inte möjligt att vänta på en foreach sådär.

Det du får göra är att fuska lite med en rekursiv funktion som anropas när ditt callback är färdigt.

Likt det här:
https://jsfiddle.net/rrp0kdbw/1/

Vill du jobba med promises så är det bara att de returnerar varandra såklart.

EDIT:

let arr = [1, 2, 3, 4, 5]; let $numbers = document.getElementById('numbers'); (function nextNumber(numbers) { if (numbers.length > 0) { let nr = numbers.shift(); setTimeout(() => { let $div = document.createElement('div'); $div.append(nr); $numbers.append($div); nextNumber(numbers); }, 500); } else { // done $numbers.append('Done'); } }(arr));

Uppdaterade även koden i jsfiddle med kommentarer:
https://jsfiddle.net/rrp0kdbw/2/

Permalänk
Medlem
Skrivet av JeanC:

Det är inte möjligt att vänta på en foreach sådär.

Det du får göra är att fuska lite med en rekursiv funktion som anropas när ditt callback är färdigt.

Likt det här:
https://jsfiddle.net/rrp0kdbw/1/

Vill du jobba med promises så är det bara att de returnerar varandra såklart.

EDIT:

let arr = [1, 2, 3, 4, 5]; let $numbers = document.getElementById('numbers'); (function nextNumber(numbers) { if (numbers.length > 0) { let nr = numbers.shift(); setTimeout(() => { let $div = document.createElement('div'); $div.append(nr); $numbers.append($div); nextNumber(numbers); }, 500); } else { // done $numbers.append('Done'); } }(arr));

Uppdaterade även koden i jsfiddle med kommentarer:
https://jsfiddle.net/rrp0kdbw/2/

Tack så mycket!

Det där ser inte omöjligt ut alls.

Vet du alls hur det skulle se ut med promises? Det känns som det skulle vara kul att lära sig och förmodligen blir det mer "läsbart". Men om inte tack för svaret!

Visa signatur

Gaemer Addicted!

Permalänk
99:e percentilen
Skrivet av we_man1:

updateSomething(){ var newArr = JSON.parse(localStorage.getItem('theData')) newArr.forEach((element)=>{ return this.updateData(element); //funktion som uppdaterar datan och return elementet }) console.log(newArr) //Detta visar korrekt data localStorage.setItem('theData', JSON.parse(newArr)); // detta visar gamla datan }

Borde inte sista raden vara:

localStorage.setItem('theData', JSON.stringify(newArr);

Sen undrar jag om du inte kan göra uppdateringen mindre imperativt och mer deklarativt?

const KEY = 'theData'; function updateData(data) { return data; // identity update } const oldData = JSON.parse(localStorage.getItem(KEY)); const newData = oldData.map(updateData); localStorage.setItem(KEY, JSON.stringify(newData));

Visa signatur

Skrivet med hjälp av Better SweClockers

Permalänk
Medlem

@we_man1: Såhär

https://jsfiddle.net/g0L2acvx/

function display(str) { let $numbers = document.getElementById('numbers'); let $div = document.createElement('div'); $div.append(str); $numbers.append($div); } function runAll(numbers) { if (numbers.length > 0) { return new Promise((resolve, reject) => { let nr = numbers.shift(); display(nr); setTimeout(() => resolve(), 500); }).then(() => runAll(numbers)); } else { return Promise.resolve(); } } let numbers = [0, 1, 2, 3, 4, 5]; return runAll(numbers).then(() => { alert('Klart'); });

Hoppas det går lättare att läsa; så har du reject där med ifall du vill säga att något blev fel.

Värt att poängtera att har du stora arrayer så är det onödigt att leta efter diven med "numbers" varje gång; hade lyft ut den i stället.

Sedan ska du ha stringify precis som @Alling skrev.

Permalänk
Medlem
Skrivet av JeanC:

@we_man1: Såhär

https://jsfiddle.net/g0L2acvx/

function display(str) { let $numbers = document.getElementById('numbers'); let $div = document.createElement('div'); $div.append(str); $numbers.append($div); } function runAll(numbers) { if (numbers.length > 0) { return new Promise((resolve, reject) => { let nr = numbers.shift(); display(nr); setTimeout(() => resolve(), 500); }).then(() => runAll(numbers)); } else { return Promise.resolve(); } } let numbers = [0, 1, 2, 3, 4, 5]; return runAll(numbers).then(() => { alert('Klart'); });

Hoppas det går lättare att läsa; så har du reject där med ifall du vill säga att något blev fel.

Värt att poängtera att har du stora arrayer så är det onödigt att leta efter diven med "numbers" varje gång; hade lyft ut den i stället.

Sedan ska du ha stringify precis som @Alling skrev.

Tack för svaren! De har hjälpt. Och ja jag skrev bara ett exempel snabbt så blev visst fel.

En sista fråga, annars har jag börjat greppa detta nu. Kan man lägga reject utan en if statement.. t.ex. Om ditt nummer array kommer från en api, och den av någon anledning inte är tillgänlig?

Visa signatur

Gaemer Addicted!

Permalänk
Medlem

@we_man1: Du kan köra reject när som helst; Promise tar hand om det och ignorerar allt efter du kört en promise ända ner till en catch eller om du har det i en metod fast som parameter nr2.

Permalänk
Medlem

Varför göra det så komplicerat? En vanlig for loop är inte asynk.

for(var i = 0; i < newArr.length; i++) this.updateData(element);

Men vem vet, jag kanske är för old school och vill ha enklaste lösning

Permalänk
Medlem
Skrivet av thrawn:

Varför göra det så komplicerat? En vanlig for loop är inte asynk.

for(var i = 0; i < newArr.length; i++) this.updateData(element);

Men vem vet, jag kanske är för old school och vill ha enklaste lösning

Tänkte precis samma. Varför hålla på med async i detta fallet då loopen kommer köras utan större vänttider, en annan grej om man gör get calls eller dylikt som faktiskt tar tid att få respons mm.

Visa signatur

Arch - Makepkg, not war -||- Gigabyte X570 Aorus Master -||- GSkill 64GiB DDR4 14-14-15-35-1T 3600Mhz -||- AMD 5900x-||- Gigabyte RX6900XT -||- 2x Adata XPG sx8200 Pro 1TB -||- EVGA G2 750W -||- Corsair 570x -||- O2+ODAC-||- Sennheiser HD-650 -|| Boycott EA,2K,Activision,Ubisoft,WB,EGS
Arch Linux, one hell of a distribution.

Permalänk
Inaktiv

.forEach är inte async, allt som händer i den kommer hända innan någon rad efter exekverar.

const numbers = [1, 2, 3, 4, 5]; numbers.forEach((number) => console.log(number)); console.log("done");

Kodsnutten kommer alltid skriva
1
2
3
4
5
done

Du sparar datan fel och läser nog in något annat eller så. Eller så skriver du över den någon annan stans

Permalänk
Medlem

jag tyckte det var skumt att forEach var async, men jag brukar inte använda den pga kompatibilitet. Så jag hade egentligen ingen koll på just den, tyckte bara föreslagna förslag var onödigt komplicerade

@we_man1: vad för updateData ? Gör den något async?

Skrivet av anon81912:

.forEach är inte async, allt som händer i den kommer hända innan någon rad efter exekverar.

const numbers = [1, 2, 3, 4, 5]; numbers.forEach((number) => console.log(number)); console.log("done");

Kodsnutten kommer alltid skriva
1
2
3
4
5
done

Du sparar datan fel och läser nog in något annat eller så. Eller så skriver du över den någon annan stans

Skickades från m.sweclockers.com

Permalänk
Medlem
Skrivet av thrawn:

jag tyckte det var skumt att forEach var async, men jag brukar inte använda den pga kompatibilitet. Så jag hade egentligen ingen koll på just den, tyckte bara föreslagna förslag var onödigt komplicerade

@we_man1: vad för updateData ? Gör den något async?

Skickades från m.sweclockers.com

Hej!

Med mitt fall så kommer "updateData" funktionen hämta data från en api. Vilket gör att det pajar för sig själv. Men om du har någon enkel lösning hur man får den att vänta på responsen så tar jag gärna emot tips

Visa signatur

Gaemer Addicted!

Permalänk
Medlem

@we_man1: Yes det har jag

Såhär brukar jag göra, it ain't pretty but it works

Har bara skrivit den här koden på frihand, men det är bara för att du ska få lite koll på logiken. Så får du skriva om den själv. Det går självklart bra att använda promises, det är väl egentligen att föredra, men jag kan inte den syntaxen i huvudet...

function fetchData(onDone){ //Hämta data och när den är hämtad så kör functionen nedan. Det går självklart att använda promises istället, det här är bara för att jag ska skriva det lite snabbare :) if(typeof onDone === "function") onDone(result) } var arr = [1,2,3,4], fetchCounter = arr.length; //håll koll på hur många requests som kommer att göras for(var i = 0; i < arr.length; i++) { fetchData(arr[i], function(res){ fetchCounter--;//här har vi fått svar på en request, så då drar vi bort en från vår variabel som håller koll på det if(fetchCounter < 1) { //Här har alla requests fått svar och är behandlade localStorage.setItem('blah'); } }); }

Permalänk
Medlem

Vad sägs om att göra en funktion som heter updateDataArray() som tar hela arrayen av data istället för bara ett element i taget, sedan när den är klar returnerar den en Promise. Då behöver du bara hantera det såhär:

const oldArr = JSON.parse(localStorage.getItem('theData')) updateDataArray(oldArr).then((resultArr) => { localStorage.setItem('theData', JSON.stringify(resultArr)); }.catch((err) => { // Något blev fel });

Permalänk
Medlem

Kanske sent svar men det låter som du behöver async/await, tror det kom med es6. Kolla gärna vidare då det hanterar asynchrona kallelser utan att behöva egna callbacks. Jag har använt det vis utveckling av react-native när mobilen har behövt accessa localstorage

Skickades från m.sweclockers.com

Permalänk
Medlem
Skrivet av Razki:

Kanske sent svar men det låter som du behöver async/await, tror det kom med es6. Kolla gärna vidare då det hanterar asynchrona kallelser utan att behöva egna callbacks. Jag har använt det vis utveckling av react-native när mobilen har behövt accessa localstorage

Skickades från m.sweclockers.com

Async är dock en del av nästa standard vill jag minnas. I så fall får man använda något som Babel.

Permalänk
Medlem
Skrivet av MrDoggo:

Async är dock en del av nästa standard vill jag minnas. I så fall får man använda något som Babel.

Dock så har du promises som kan användas redan idag. Finns miljontals exempel på nätet både med och utan Jquery.

Nu är frågan om du hämtar något via ett API gör du ett stort call eller flera små? Värt är att webbläsaren har en viss limit på hur många olika calls den kan göra. Dock gör du flera calls i en loop så bör du bryta ut dessa och göra alla på samma gång och sedan vänta tills du fått svar och antingen processa när alla är klara eller om det går börja arbeta direkt med dessa.

Visa signatur

Arch - Makepkg, not war -||- Gigabyte X570 Aorus Master -||- GSkill 64GiB DDR4 14-14-15-35-1T 3600Mhz -||- AMD 5900x-||- Gigabyte RX6900XT -||- 2x Adata XPG sx8200 Pro 1TB -||- EVGA G2 750W -||- Corsair 570x -||- O2+ODAC-||- Sennheiser HD-650 -|| Boycott EA,2K,Activision,Ubisoft,WB,EGS
Arch Linux, one hell of a distribution.

Permalänk
Medlem

En for-loop kommer inte vänta på att ett svar från en HTTP-request körs klart innan den kör vidare vilket jag tolkade hans fråga som.

Permalänk
Medlem

Här är en enkel json fetch där du låter två separata promises köras och returnerar den som blir klar först (antingen får du json från servern eller så körs en timeouts om det tog för lång tid).

fetchJSON(server, timeout) { let timer = new Promise((resolve, reject) => { setTimeout(reject, timeout, 'request timed out'); }); let f = new Promise((resolve, reject) => { fetch(server, { method: 'GET', dataType: 'json', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }}) .then(response => response.json()) .then(json => resolve(json)) .catch(reject) }); return Promise .race([timer, f]) .then(json => this._successConnection(json)) .catch(err => this._errorConnection(err)); } _successConnection(json) { return json; } _errorConnection(err) { return false; }

Permalänk
Medlem
Skrivet av Commander:

Dock så har du promises som kan användas redan idag. Finns miljontals exempel på nätet både med och utan Jquery.

Är medveten eftersom det var Promise jag nämnde och kom med exempel på i posten innan. Svarade bara på att Async som den andra skribenten nämnde (som antagligen kommer ersätta Promise i många fall) kräver Babel just nu.

Permalänk
Skrivet av Alling:

Sen undrar jag om du inte kan göra uppdateringen mindre imperativt och mer deklarativt?

const KEY = 'theData'; function updateData(data) { return data; // identity update } const oldData = JSON.parse(localStorage.getItem(KEY)); const newData = oldData.map(updateData); localStorage.setItem(KEY, JSON.stringify(newData));

Det här känns som den bästa lösningen.