• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

react wielokrotna zmiana state'a w funkcji asynchronicznej z await

Object Storage Arubacloud
+1 głos
98 wizyt
pytanie zadane 13 grudnia 2020 w JavaScript przez hakiros54 Obywatel (1,160 p.)

Witam, próbuje zrobić w react'cie stronę w której użytkownik wybiera metodę sortowania a następnie zgodnie z wybranym algorytmem strona sortuje divy. Żeby nie robiło się to natychmiastowo funkcja ma w sobie await sleep(). 

O ile sortowanie działa poprawnie tak state się nie zmienia (raz się zmienia, raz nie zmienia, czasem sortuje tylko część divów, czasami w ogóle). Prawdopodobnie problem jest spowodowany właśnie await sleep(), bez niego wszystko dobrze działa ale nie wiem ani dlaczego to się psuje przez await sleep, ani tym bardziej jak to naprawić, a potrzebuje tego do poprawnego pokazania jak działają te algorytmy. 

  const [items, setItems] = useState([52,51,55,54,50,53,57,56,59,58]);

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
const bubble = async () =>{
    let itemsArray = [...items];
    let buffer;
    for(let i = items.length - 1; i > 0; i--){
      for(let j = 0; j < i; j++){
        if(itemsArray[j] > itemsArray[j+1]){
          buffer = itemsArray[j];
          itemsArray[j] = itemsArray[j+1]
          itemsArray[j+1] = buffer;
          await sleep(100);
          setItems(itemsArray);
        }
      }
    }
  }

 

komentarz 13 grudnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Pokaż gdzie wołasz funkcję bubble.

komentarz 13 grudnia 2020 przez hakiros54 Obywatel (1,160 p.)
  const startSorting = (e) => {
    e.preventDefault();
    switch(sortType){
      case "bubble":
        bubble();
        break;
      case "quick":
        quick();
        break;
      case "merge":
        merge();
        break;
    }
  }
<form>
	<input 
		type = "number" 
		id = "howMany" 
		value = {howMany}
		name = "howMany" 
		placeholder = "How many elements" 
		onChange = {e => {setHowMany(e.target.value); prepareItems(e.target.value);}} 
	/>
  	<select name="sortType" id="sortType" value = {sortType} onChange = {e => setSortType(e.target.value)}>
    	<option value="bubble">Bubble sort</option>
    	<option value="quick">Quick sort</option>
    	<option value="merge">Merge sort</option>
  	</select>
  	<select name="speed" id="speed" value = {speed} onChange = {e => setSpeed(e.target.value)}>
    	<option value="slow">Slow</option>
    	<option value="normal">Normal</option>
    	<option value="fast">Fast</option>
  	</select>
  	<button onClick = {startSorting}>Start</button>
</form>

 

1 odpowiedź

0 głosów
odpowiedź 13 grudnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Funkcja bubble jest asynchroniczna, co oznacza, że kod w funkcji startSorting (tam gdzie wołasz bubble) będzie wykonywać się dalej (nie czekając na to aż sleep w bubble się skończy). Jeśli w trakcie operacji asynchronicznej (jak np. setTimeout, którego używasz) wykonuje się inny kod, który odczytuje lub zmienia stan tablicy items, to właśnie może nastąpić rozjazd, o którym napisałeś.

Możesz na czas manipulacji tą tablicą ustawić flagę, którą będziesz sprawdzać w innych miejscach operujących na tej tablicy, żeby tego nie robiły, dopóki trwa sleep.

1
komentarz 14 grudnia 2020 przez hakiros54 Obywatel (1,160 p.)

state items jest zmieniany tylko w dwóch miejscach - w funkcji bubble oraz w funkcji która wstawia losowe liczby z danego przedziału do tej tablicy. Ta druga odpala się tylko przy podawaniu ilości elementów do sortowania i faktycznie, może to powodować buga ale w tym wypadku musi chodzić o coś innego, bo w trakcie sortowania nie zmieniam ilości elementów. Dodatkowo program nie działa nawet gdy sleep podaje tylko 1 a elementów jest bardzo mało. 

Wydaje mi się, że problem może być powiązany z tym, że chyba hooki w react'cie też są asynchroniczne ale za mało znam reacta by wiedzieć co dokładnie może generować ten problem. Próbowałem też używać 

await setItems(itemsArray);

licząc, że funkcja będzie się dalej wykonywała ale to też nic nie dawało.

komentarz 14 grudnia 2020 przez ScriptyChris Mędrzec (190,190 p.)

Wydaje mi się, że problem może być powiązany z tym, że chyba hooki w react'cie też są asynchroniczne ale za mało znam reacta by wiedzieć co dokładnie może generować ten problem

Racja. Nie zorientowałem się, że tam jest podwójna pętla. Rozbieżność może być między ustawianiem nowego stanu tablicy a kolejnymi iteracjami pętli.

Możesz użyć hooka useEffect, który będzie odpalać callback, gdy items się zmieni - umieść tą zmienną w tablicy jako drugi parametr do useEffect. Dodatkowo utwórz sobie Promise i wyciągnij z niego funkcję resolvującą - tym będziesz monitorować moment aktualizacji zmiennej items.

let itemsUpdated = Promise.resolve(false); // nadpisywalny promise
let itemsUpdateResolver = () => {}; // nadpisywalny resolver promise'a

useEffect(() => {
  if (items) {
    itemsUpdateResolver(true); // zresolvuj promisa
  }
}, [items]); // odpalaj hooka z każdą aktualizacją zmiennej items

const bubble = async () =>{
    let itemsArray = [...items];
    let buffer;

    for(let i = items.length - 1; i > 0; i--){
      for(let j = 0; j < i; j++){

        if(itemsArray[j] > itemsArray[j+1]){
          buffer = itemsArray[j];
          itemsArray[j] = itemsArray[j+1]
          itemsArray[j+1] = buffer;
         
          await sleep(100);

          itemsUpdated = new Promise(resolve => itemsUpdateResolver = resolve); // ustaw nowego promisa i nadpisz resolver od nowa
          setItems(itemsArray);

          await itemsUpdated; // czekaj na resolve promisa. On oznaczy, że items zostało zaktualizowane i pętla będzie mogła lecieć dalej
        }
      }
    }

Nie testowałem tego kodu - na logikę powinien działać. Na pewno jest lepszy sposób na ogarnięcie tego, ale to wymyśliłem na szybko.

Podobne pytania

0 głosów
1 odpowiedź 218 wizyt
pytanie zadane 6 lipca 2022 w JavaScript przez zerakot Obywatel (1,870 p.)
0 głosów
1 odpowiedź 199 wizyt
0 głosów
2 odpowiedzi 121 wizyt
pytanie zadane 17 marca 2020 w JavaScript przez mreo Użytkownik (790 p.)

92,555 zapytań

141,404 odpowiedzi

319,557 komentarzy

61,940 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...