Hejsan! Problemet är snarare i JS-koden. Webservicen får ett anrop och kommer svara - att bygga runt det är ju ett jävla jobb. JS-koden kan ju däremot säga att "mitt tidigare anrop var bara bullshit, det är det senaste jag vill ha". Har du skrivit JS själv, eller är det genererat? Jag har skrivit den själv, den är på 9 rader :). Jag antar att du gör sjävla uppdateringen i onComplete? Jag är med på principen och återkommer med resultat eller fler tankar... :) En helt annan tanke: Har en "global" variabel i js som du använder ungefär såhär: Det förbättrar ju lite, men (om jag inte missförstått - lunchkoma): Efter lite mer funderande så har jag fått mer förståelse för problemet i sig och problem med lösningarna här :) (döda mig nu inte om jag missförstått ;)). Saken är att med Niklas lösning så hämtar man fortfarande en massa från servern men väljer bara att visa lite av det, vilket inte är optimalt för användaren får vänta. Problemet med Oskars är väl ungefär samma, användaren får vänta onödigt länge. 100 ms är nog i sammanhanget, men vid en snabb dragning med musen så kommer det spara väldigt mycket. Det jag tänker är att man kombinerar med Niklas variant. <b>>Problemet med Oskars är väl ungefär samma, användaren får vänta onödigt länge.</b> Man kan även tänka sig en lösning där timeouten börjar på 100 ms, men om det kommer flera väldigt nära inpå varandra så kan man börja dra upp timeouten uppemot kanske 800-1000 ms? Jo, precis - det ligger ju nära tillhands med en adaptiv lösning. Nu funkar det!Asynkrona rop till MySql
Jag har ett problem.
Min tanke är att när användaren för muspekaren över en div i en listview så skickas ett anrop till databasen som hämtar specifik data och visar den på sidan.
Allt detta fungerar MEN om man drar över många divar snabbt så ser man tydligt att anropen köas upp och visnings-diven visar olika värden under flera sekunder efteråt. Det jag vill är att om ett nytt anrop kommer så ska de andra tidigare avslutas. Lite som UDP kontra TCP :).
För att göra detta använder jag en listview med divar i som jag lagt till mouseover-event på. Javascript kallar på en metod i en webservice som sedan gör MySql-anropet. Anropet är till en stored procedure i MySql.
Jag vet inte om problemet är i ASP.NET-koden eller i MySql, men jag chansar på här :).Sv: Asynkrona rop till MySql
Sv:Asynkrona rop till MySql
Koden ser ut som följer:
function updateSummary(subCat) {
var lw = $get('itemPlaceHolderDiv');
var list = lw.getElementsByTagName("input");
var array = new Array();
for (var i = 0; i < list.length; i++) {
if (list[i].type == 'checkbox' && list[i].checked == true) {
array.push(list[i].value);
}
}
SummaryService.getSummary(subCat,array.toString(),onComplete,onFailed);
}
Har du något förslag på hur man skulle kunna skriva?Sv: Asynkrona rop till MySql
Då borde du kunna utöka den med
onComplete(WhenWasThisSent)
och skicka med det a'la delegat. Kan inte exakta syntaxen, men något i stil med:
latestupdate = now()
SummaryService.getSummary(subCat,array.toString(),new function(){ return onComplete(latestupdate )}, onFailed);
Och sen utöka gamla oncomplete till:
function onComplete(WhenWasThisSent){
if(WhenWasThisSent < latestupdate)
return;
gammal kod
}
Är du med på principen?Sv:Asynkrona rop till MySql
Sv: Asynkrona rop till MySql
var updateTimeoutReference = false; //global variabel
onclick="updateFromServer(this);"
function updateFromServer(obj)
{
if(updateTimeoutReference)
clearTimeout(updateTimeoutReference);
updateTimeoutReference = setTimeout(function()
{
updateTimeReference = false;
alert(obj.id); //här ska du ha ditt riktiga anrop istället
}, 100);
}
Vad du gör är att du sätter en timer på ett lågt värde (t.ex. 100 ms), och först efter 100 ms så kontaktar du servern. Dessutom så avbryter du en gammal timeout om en sådan finns. Vad som kommer hända är att om man hinner gå över till en annan inom 100 ms så kommer servern aldrig att kontaktas. Det sparar ganska mycket både för servern och klienten
Sv:Asynkrona rop till MySql
1. Dels får man ju en liten nersegning. 100 ms måste ju anpassas efter hur man drar musen och hur långsam uppkoppling det är, och blir det 500 ms istället så kommer det ju ge en impact.
2. Dels löser man ju inte problemet i de fallen då uppkopplingen är för långsam, då kommer fortfarande grejerna efter varandra. Och i teorin kan du ju få två "consecutive events" som ligger i fel ordning?
Det jag antar då är att det blir något i stil med:
Click--|---- 100 ms ----|- Anrop -------------------------|- Visa resultat -|
....................................Click -- |---- 100 ms ----|- Anrop -- ...
Om detta går att förstå =)
Däremot borde man kunna göra en combo. Eller är det kanske möjligt att avbryta själva anropet?Sv:Asynkrona rop till MySql
Det jag vill göra är att kunna kapa i kön så att allt inte körs. Jag har hittat en blogg-post med tillhörande artiklar, men eftersom jag inte är så high på javascript så går det lite segt :/.
http://siderite.blogspot.com/2008_02_01_archive.html
Det ska tydligen finnas en array "window._webRequests" som innehåller köade webförfrågningar. Och ideen är att man loopar igenom den och försöker döda dem om det går. Mitt problem tror jag är att i bloggens exempel så dödas en startad, men jag vill döda en startad och göra att de andra som inte är startade än inte kommer att startas heller, detta för att sedan lägga till den senaste i kön (som förhoppningsvis körs direkt då).
Min modifierade kod är nu
function updateSummary(subCat) {
...
if (!window._webRequests) {
window._webRequests = Array();
}
KillRequests();
_webRequests[_webRequests.length] = SummaryService._staticInstance.getSummary(subCat, array.toString(), onComplete, onFailed);
}
function KillRequests() {
if (!window._webRequests) {
return;
}
for (var c = 0; c < window._webRequests.length; c++) {
if (window._webRequests[c]) {
var executor = window._webRequests[c].get_executor();
if (executor.get_started()) {
executor.abort();
}
}
}
}
function onComplete(args) {
$get(tbSummary).value = args;
}
function onFailed(args) {
$get(tbSummary).value = args._message;
}Sv: Asynkrona rop till MySql
Sv: Asynkrona rop till MySql
Nja. Oskars lösning ser till att det inte blir nya anrop förrän det verkar som att användaren har bestämt sig. Det blir samma problem som originalet om användaren är "halvlångsam" (dvs. fortare än js-timeouten men snabbare än själva webbanropet).
Med lite tweaking på 100ms så tror jag nog att Oskars fungerar bättre än min, men det är nog svårt att få något som alltid funkar bra med den,.
Vad gäller din lösning: Yes, det är "rätt" lösning, förutsatt att den fungerar - avbryta utestående anrop. Det kombinerar du med fördel med Oskars metod.Sv:Asynkrona rop till MySql
Sv: Asynkrona rop till MySql
Spontan tanke är att hela tiden försöka hålla koll på hur lång tid ett anrop tar, och sen köra exponential decay eller va fan det kallas, typ:
init(){
Estimate = 100;
lambda = 0.7
}
onComplete(){
RealTime = GetThisCallTime();
Estimate = lambda * Estimate + (1-lambda)*RealTime
}
Estimate innehåller då hela tiden "typ" så lång tid som ett anrop tar. En enkel lösning då är att alltid ha en timeout på säg en tiondel av det, om man bara tänker på klienten och struntar i hur servern mår.
Och man kan ju även göra mer avancerade varianter där man kombinerar detta med information om hur användaren verkar bete sig, som du föreslår. Men då får man nog ha en situation typ google fast på en enda server för att det ska löna sig... =)Sv:Asynkrona rop till MySql
Det blev en tvådelad lösning.
Det första problemet var att det kom många anrop (som jag skulle döda), detta berodde på att jag använde mouseover som triggas hur många gånger som helst. Så den första delen av lösningen var att skapa ett mouseEnter-event som bara finns i IE...
Den andra delen av problemet var att om användaren drar snabbt så skickas många requests, detta löste jag med Oskars förslag med en fast tid på 150ms.
Jag kanske ger mig på att få väntetiden adaptiv, men det är inte säkert :).
Tack för alla svar
Här är koden för andra som vill se:
var currentElement;
var latestUpdate;
var updateTimeoutReference = false;
//Create mouseEnterEvent
function addEvent(_elem, _evtName, _fn, _useCapture) {
currentDiv = _elem;
if (typeof _elem.addEventListener != 'undefined') {
if (_evtName === 'mouseenter') {
_elem.addEventListener('mouseover', mouseEnter(_fn, _elem), _useCapture);
}
else if (_evtName === 'mouseleave') {
_elem.addEventListener('mouseout', mouseEnter(_fn), _useCapture);
}
else{
_elem.addEventListener(_evtName, _fn, _useCapture);
}
}
else if (typeof _elem.attachEvent != 'undefined') {
_elem.attachEvent('on' + _evtName, _fn);
}
else {
_elem['on' + _evtName] = _fn;
}
}
function mouseEnter(_fn, _elem) {
return function(_evt) {
var relTarget = _evt.relatedTarget;
if (this === relTarget || isAChildOf(this, relTarget)){ return; }
_fn.call(this, _evt, _elem);
}
};
function isAChildOf(_parent, _child) {
if (_parent === _child) {
return false;
}
while (_child && _child !== _parent) {
_child = _child.parentNode;
}
return _child === _parent;
}
//Summary functions
function updateFromServer(e, elem){
if (updateTimeoutReference) {
clearTimeout(updateTimeoutReference);
}
updateTimeoutReference = setTimeout(function() {
updateTimeReference = false;
updateSummary(elem);
}, 150);
}
function updateSummary(elem) {
var subCat = elem.attributes.getNamedItem("name").value;
var lw = $get('itemPlaceHolderDiv');
var list = lw.getElementsByTagName("input");
var array = new Array();
for (var i = 0; i < list.length; i++) {
if (list[i].type == 'checkbox' && list[i].checked == true) {
array.push(list[i].value);
}
}
SummaryService.getSummary(subCat, array.toString(), onComplete, onFailed);
}
function onComplete(args) {
$get(tbSummary).value = args;
}
function onFailed(args) {
$get(tbSummary).value = args._message;
}