Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> jak dokładnie działa abort() w jQuery?
SeaDog
post 26.09.2014, 19:44:42
Post #1





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Witam, ostatnio napisałem post w dziale mysql (dublowanie zapytań SQL). W skrypcie był błąd, który został poprawiony i sytuacja się poprawiła,
ale ostatnio dostałem info od swojego klienta, że po wypełnieniu formularza znów dublują się zapytania w bazie danych MySQL.
Forumowicze, mam do Was pytanie.

Jak dokładnie działa funckja abort() w jQuery? Chodzi mi konkretnie o ustawiony timeout w Ajaxie i anulowaniu requestu w sekcji error.

Już tłumaczę o co mi dokładnie chodzi, otóż:

Użytkownik wypełnia formularz, jeśli wszystkie pola zostaną wypełnione a walidacja nie zwróci błędu do skryptu PHP leci ciąg JSON.
W metodzie AJAX ustawiony jest timeout na 3 sekundy. Po trzech sekundach od nie otrzymania success uruchamia się funkcja z informacją dla użytkownika,
że jego zlecenie jest przetwarzane (w tym momencie AJAX zwrócił error i ponawia wysłanie ciągu JSON do tego samego skryptu PHP w przeciągu
jednej sekundy i tak w kółko... aż do momentu wyrobienia się przeciągu 3 sekund, czyli AJAX success).

Wszystko zależne jest od obciążenia serwera. Jeśli obciążenie jest znikome, komunikat o przetważaniu dancyh nawet się nie włącza. Jeśli obciążenie jest
nieco wyższe, wtedy funckja uruchamia się po trzech sekundach. Bywa też tak, że komunikat się pokaże, po chwili zniknie a wpis się nie zdublował.
Głowię się już nad tym około tygodnia. Jak to wygląda w praktyce?

Czy możliwym jest, że wysłany ciąg JSON leci do serwera, w bazie kolejkowane są zapytania, minęły 3 sekundy, w tym momencie uruchamia się wyżej
opisana funckja i ponawiane jest to samo zapytanie mimo tego, że wpis już został prawidłowo dodany? czyli tak jakby funckja wykonuje się ponownie,
ponieważ AJAX nie zdążył zwrócic success?

Efekt ma być taki, żeby najpóźniej po 3 sekundach od kliknięcia w przycisk "Wyślij", jeśli skrypt jeszcze przetwarza dane ponieważ serwer jest obciążony,
użytkownik otrzymał odpowiednią informację, żeby nie pomyślał, że coś się zawiesiło albo coś podobnego. Myślałem, że użycie w tym wypadku
AJAX error jest dobrym rozwiązaniem ale zauważyłem, że pomimo 3 zabezpieczeń takich jak

- zdjęcie zdarzenia z przycisku po jego kliknięciu,
- przypisanie wartości zmiennej "s" = 1, która powoduje, że ponowny request nie może być wysłany. Wartość 0 wraca po otrzymaniu AJAX success,
- zabezpieczenie skryptu PHP sesją, która pozwala wysłać następne zlecenie dopiero po 5 sekundach

...nadal występuje problem z dulowaniem zleceń,

Proszę o pomoc.

Ten post edytował SeaDog 26.09.2014, 19:48:48
Go to the top of the page
+Quote Post
trueblue
post 27.09.2014, 18:23:16
Post #2





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


W jakim momencie uruchamiasz 2 pierwsze zabezpieczenia?


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 28.09.2014, 17:57:40
Post #3





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Pierwsze zabezpieczenie (zdjęcie zdarzenia z przycisku) następuje po kliknięciu w przycisk "Wyślij" przed wysłaniem zapytania AJAX.
Drugie zabezpieczenie to ustawienie wartości 1 dla zmiennej sprawdzającej czy dane zostały wysłane. Formularz można wysłać co 5 sekund.
Jeśli AJAX zwróci success, wtedy zmienna otrzymuje wartość 1 i w tym samym momencie uruchomiony zostaje setTimeout(function(){zmienna=0}, 5000);
Zauważyłem, że wpisy się dublują ale czas dodania wpisu pomiędzy pierwszym a drugim wpisem jest różny o 5 sekund.
Wychodzi na to, że pomimo anulowania requestu, ten nadal próbuje wysłać dane do momentu zwolnienia blokady tj. zmienna=0 i wtedy wpis jest ponownie
dodany.
Te dziwne rzeczy dzieją się w momencie obciążenia serwera, kiedy timeout ustawiony jest na 3 sekundy po czym jQuery ponawia próbę wysłania
tych samych danych.
Zastanawiam się jak to dokładnie działa, ale w tym wypadku chyba tak, że mysql tworzy kolejkę INSERTÓW, w między czasie mijają 3 sekundy,
skrypt jQUERY nie otrzymał success więc ponawia próbę raz jeszcze a tak naprawdę wpis już się dodał. Dobrze rozumuję?
Go to the top of the page
+Quote Post
trueblue
post 28.09.2014, 18:17:32
Post #4





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Ok, to wróćmy może do abort.
Gdzie wywołujesz?


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 28.09.2014, 18:34:48
Post #5





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


  1. $(document).ready(function(){
  2.  
  3. var form_data = $('#form').serialize();
  4.  
  5. function send(){
  6.  
  7. req = $.ajax({type:"POST",url:"skrypt.php",dataType:"json",data:form_data,timeout:3000,success:function(){
  8.  
  9. // tutaj wykonywany jest kod po prawidłowym dodaniu wpisu
  10.  
  11. ...
  12.  
  13. },error:function(){
  14.  
  15. // W tym miejscu anulowany jest request i następuje ponowne wysłanie danych z opóźnieniem jednej sekundy
  16.  
  17. req.abort();
  18. setTimeout(function(){send();}, 1000);
  19.  
  20. }
  21.  
  22. }
  23.  
  24. send();
  25.  
  26. });


Ten post edytował SeaDog 28.09.2014, 18:38:59
Go to the top of the page
+Quote Post
trueblue
post 28.09.2014, 18:46:53
Post #6





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Spróbuj tak:


  1. $(document).ready(function(){
  2. var req=null;
  3. var timer=null;
  4.  
  5. function send(){
  6.  
  7. if(req!==null) req.abort();
  8. if(timer!==null) clearTimeout(timer);
  9.  
  10. var form_data = $('#form').serialize();
  11.  
  12. req = $.ajax({type:"POST",url:"skrypt.php",dataType:"json",data:form_data,timeout:3000,success:function(){
  13.  
  14.  
  15. // tutaj wykonywany jest kod po prawidłowym dodaniu wpisu
  16.  
  17. ...
  18.  
  19. },
  20. error:function(xhr,status,error){
  21.  
  22. // W tym miejscu anulowany jest request i następuje ponowne wysłanie danych z opóźnieniem jednej sekundy
  23. if(status=="timeout") setTimeout(function(){send();}, 1000);
  24. }
  25. }
  26. send();
  27. });


P.S. Nie wiem właściwie czemu opóźniasz wywołanie send.

Ten post edytował trueblue 28.09.2014, 18:47:21


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 28.09.2014, 18:57:33
Post #7





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Zmienna timer jest chyba nie potrzebna, ponieważ setTimeout(function(){send();}, 1000); nie jest przypisane do zmiennej.
Opóźniam ze względu na obciążenie serwera. Zapytania są krótkie, lecz ich ilość jest spora i pomyślałem, że to będzie dobre rozwiązanie.
Użyłem też opóźnienia, ponieważ już wcześniej miałem problem z dublami i myślałem, że request nie został jeszcze anulowany
a już nastąpiła próba kolejnego wysłania.

Dzięki za odpowiedź ale nie uważasz, że oba rozwiązania są do siebie bardzo podobne a wynik będzie taki sam?
W Twoim rozwiązaniu request jest anulowany po otrzymaniu statusu timeout, w moim po zwróceniu erroru,
w którym timeout też jest chyba uwzględniony? Co oznacza dokładnie nagłówek xhr?

Może wystarczyłoby użycie tylko tego: "xhr,status,error" w funckji error?
Sorki, że tak dociekam, ale chcę to rozłożyć na czynniki pierwsze.

Ten post edytował SeaDog 28.09.2014, 19:07:50
Go to the top of the page
+Quote Post
trueblue
post 28.09.2014, 19:13:15
Post #8





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Tak. Powinno być:
timer=setTimeout(function(){send();}, 1000);

Są podobne. Przy czym Ty nie czyścisz clearTimeout. A czy wynik będzie taki sam, nie wiem, warto sprawdzić.
Ale mam wrażenie, że duplikaty powstają w wyniku dwukliku na przycisku.
Otwórz narzędzia developerskie przeglądarki i popatrz co się dzieje w zakładce Sieć, czy requesty są faktycznie przerywane, co się dzieje na dwuklik.


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 28.09.2014, 19:53:07
Post #9





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Oczywiście, masz rację z clearTimeout(); tylko ja nie przypisuję opóźnienia do zmiennej. Tę funkcje wykonuję raz, więc nie ma nic do czyszczenia.
Sprawdzałem co się dzieje podczas requestów. Są anulowane. Na maszynie wirtualnej na której postawiłem aplikację, wszystko przebiega prawidłowo.

Całość wykonuję tak:

1) wypełniam formularz -> kilkam przycisk -> zlecenie zostaje dodane (jeden wpis),
2) na maszynie wirtualnej na firewalu blokuję ruch przychodzący -> wypełniam formularz -> kilkam przycisk -> request po 3 sekundach jest anulowany,
po chwili następuje próba wysłania. Czekam kilka sekund i to samo, anulowany i znowu próba wysłania, odblokowuję ruch przychodzący na VM
i mam jeden wpis. Testowałem to wiele razy, u mnie wszystko gra. Sztucznie też obciążałem VM i też było dobrze.

Problem występuje na serwerze produkcyjnym, kiedy większy Load Average utrzymuje się przez kilka sekund dłużej na tym samym poziomie.

Jeśli chodzi o dwuklik to odpada, ponieważ zaraz po kliknięciu w przycisk zdejmuję z niego zdarzenie. To też testowałem poprzez szybkie klikanie enterem.
Zawsze idzie jeden request.


Odnośnie sprawdzenia, powiem tak. Problem jak narazie jest rozwiązany. Komunikat dla użytkownika odnośnie procesu przetwarzania wyświetlany jest przez
osobną funkcję 3 sekundy po kliknięciu w przycisk "Wyślij". Funckja ta przypisana jest do zmiennej czyli w=setTimeout(){.....}
Na tym samym poziomie dane są wysyłane do skryptu PHP. Timeout ustationy jest na 15 sekund. Do tego momentu komunikat wisi a w divie widać loading (gif).
Jeśli AJAX zwróci success w przeciągu 3 sekund, komunikat w ogóle się nie wyświetli, ponieważ użyta zostaje funckja clearTimeout(w).
Jeśli minie 15 sekund od kliknięcia w przycisk, request jest anulowany i komunikat zostaje zmieniony na: Przekroczono limit czasu połączenia z przyciskiem
"Spróbuj ponownie". Użytkownik sam musi ponowić próbę wysłania tych samych danych klikając w przycisk.

Jest to krótko mówiąc wydłużenie timeoutu i sposobu wyświetlania komunikatu już nie w momencie otrzymania AJAX error, a poprzez osobną funkcję,
która uruchamia się 3 sekundy po kliknięciu w przycisk. Mam nadzieję, że sytuacja się nie powtórzy bo wtedy to już nie wiem co zrobić.
Napisałem tego posta, żeby dowiedzieć się jak dokładnie działa jQuery Abort(); W moim przypadku chyba jest tak, jak napisałem wcześniej.
Baza robi kolejkę zapytań, wykonuje jedno po drugim. AJAX w przeciągu 3 sekund nie otrzymał odpowiedzi i ponawia próbę wysłania danych mimo tego,
że do bazy danych wpis został już dodany i po zmniejszeniu Load Average, drugi request zwraca success w przeciągu 3 sekund i vuala, mamy dwa takie same wpisy.

Jeśli się mylę, czekam na wytłumaczenie.

Ten post edytował SeaDog 28.09.2014, 20:12:40
Go to the top of the page
+Quote Post
trueblue
post 28.09.2014, 21:13:10
Post #10





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Które rozwiązanie eliminuje duplikaty?


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 28.09.2014, 21:51:04
Post #11





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Twojego rozwiązania nie testowałem. Myślałem, że otrzymam konkretną informację dlaczego tak się dzieje. Jak narazie nie będę wprowadzał kolejnych zmian,
ponieważ na chwilę obecną duplikatów nie ma od momentu, kiedy użyłem innego rozwiązania czyli, zamiast wyświetlać komunikat użytkownikowi kiedy Ajax
zwroci error po 3 sekundach i ponowić wysłanie danych, rozwiązałem to w ten sposób, że komunikat zwracany jest po 3 sekundach od momentu kliknięcia
w przycisk ale jako osobna funckja a timeout ustawiony jest na 15 sekund. W przypadku zwrócenia przez Ajax error, użytkownik musi kliknąć przycisk, żeby
ponownie wysłać dane.

Mniej więcej wygląda to taK;

  1. $(document).ready(function(){
  2.  
  3. var s=0;
  4.  
  5. function wait(){
  6.  
  7. $('button','#form').attr('disabled','disabled');
  8.  
  9. w=setTimeout(function(){
  10.  
  11. wai = 1;
  12.  
  13. $('#modal').dialog({
  14. autoOpen: 'true',
  15. modal: 'true',
  16. resizable: 'false',
  17. width: '30%',
  18. title: "Komunikat z serwera",
  19. show: { effect: "scale", direction: "origin" },
  20. hide: { effect: "scale", direction: "origin" },
  21. position: ['5%',120],
  22. closeOnEscape: 'false',
  23. });
  24.  
  25. $('.ui-dialog-titlebar-close').hide();
  26.  
  27. $('#modal').empty().css('text-align','center');
  28. $('#modal').html("<div style='width:100%;height:80px;background-position: center;background-repeat:no-repeat;background-image:url(\"images/loading.gif\")'></div>\
  29. Czekaj, trwa przetwarzanie informacji...");
  30.  
  31. }, 3000);
  32.  
  33. }
  34.  
  35.  
  36. var form_data = $('#form').serialize();
  37.  
  38. $('#form').submit(function(){
  39.  
  40. wai = 0;
  41.  
  42. wait();
  43.  
  44. function add(){
  45.  
  46. if(s<1){
  47.  
  48. s=1;
  49.  
  50. var req = $.ajax({type:"POST",url:"skrypt.php",dataType:"json",data:form_data,cache:false,timeout:15000,success:function(data){
  51.  
  52. if(wai>0){ $('#modal').dialog('close'); }
  53.  
  54. ...
  55.  
  56. clearTimeout(w);
  57. setTimeout(function(){s=0}, 5000);
  58.  
  59. },error:function(){
  60.  
  61. req.abort();
  62.  
  63. $('#modal').html("<b>Przekroczono limit czasu połączenia:</b><br><button type='submit' id='retry' class='btn btn-primary btn-large'>Spróbuj ponownie</button>");
  64.  
  65. $('#retry').one('click', function(){
  66.  
  67. $('#modal').dialog('close');
  68.  
  69. s=0;
  70. wai=0;
  71. wait();
  72. add();
  73.  
  74. });
  75.  
  76. }
  77.  
  78. });
  79.  
  80. }
  81.  
  82. }
  83.  
  84. add();
  85.  
  86. });
  87.  
  88. });


Ominięcie problemu, ale zagadka nadal nie jest rozwiązana...

Ten post edytował SeaDog 28.09.2014, 22:15:11
Go to the top of the page
+Quote Post
trueblue
post 29.09.2014, 14:18:04
Post #12





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Cytat(SeaDog @ 28.09.2014, 22:51:04 ) *
Myślałem, że otrzymam konkretną informację dlaczego tak się dzieje.

Trudno dawać konkretną odpowiedź mając w tym przypadku "suchy" kod.
Metoda abort nie zapewnia przerwania komunikacja z serwerem, bo może być również wykonana w momencie kiedy XMLHttpRequest ma status "done". Wtedy nie jest wywoływane zdarzenie onreadystatechange, ale to co miało pójść do serwera, poszło.
Innym rozwiązaniem jest użycie synchronicznych requestów, przy czym trzeba mieć na uwadze, że mogą one blokować możliwość interakcji użytkownika ze stroną w czasie działania. Ale masz pewność, że pójdzie jeden za drugim, w takiej kolejności jak wynika z kodu.


--------------------
Go to the top of the page
+Quote Post
SeaDog
post 30.09.2014, 09:51:19
Post #13





Grupa: Zarejestrowani
Postów: 42
Pomógł: 0
Dołączył: 15.11.2010

Ostrzeżenie: (0%)
-----


Dziękuję bardzo za pomoc. Zapoznam się jeszcze z synchronicznymi zapytaniami.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 19.04.2024 - 22:01