简体   繁体   English

Javascript:为什么我的即时搜索功能运行滞后,如何改进?

[英]Javascript: why is my instant search function running lagging, how to improve?

The demo below is the instant search function I build, just like google search, printing out result right after the user's input. 下面的演示是我构建的即时搜索功能,就像谷歌搜索一样,在用户输入后立即打印出结果。

When testing, it runs smoothly on localhost, but on the web, it runs lagging, that is, when user typing in multiple characters fast, the keypress will not show in input box for some seconds to wait for data processing (I guess) then the charactered typed showed many seconds after . 在测试时,它在localhost上运行顺畅,但在网络上,它运行滞后,也就是说,当用户快速输入多个字符时,按键不会在输入框中显示几秒钟等待数据处理(我猜)然后特征显示在几秒钟后显示出来。 Absolutely this is no good for user's experience. 这绝对不利于用户的体验。 How may I resolve this? 我怎么解决这个问题?

Frontend 前端

<input onkeyup="searchq();" type="text">
<script>
function searchq(){
        // get the value
            txt = $("input").val();
            // post the value
            if(txt){
                $.post("search.php", {searchVal: txt}, function(result){
                    $("#search_output").html(result+"<br><a class='anchor_tag_s' href='createobject.php?object="+txt+"'><div id='notfound'>Not found above? Create Here.</div><a>");
                });
            }
            else{
                $("#search_output").html("");
            }
        };
</script>

Backend 后端

    //query the search using match
    $query=mysqli_query($conn,"SELECT * from objects WHERE Match(name) Against ('%$search%' in natural language mode) LIMIT 9") or die("could not search! Oops, Panpan might be hacked");
    $count=mysqli_num_rows($query);
   //if match no result using like query
    if($count==0){
        $query=mysqli_query($conn,"SELECT * from objects WHERE name LIKE '%$search%' LIMIT 9") or die("could not search! Oops, Panpan database might be hacked");
        $count=mysqli_num_rows($query);
        if ($count==0){
            while($count==0){

            //if matach and like show no result, remove the frist word of array, then search, if no result, do it again, if not, do it again.
                $search = explode(" ",$search);
                $search=array_slice($search,1,2);
                $search=implode(" ",$search);
                $query=mysqli_query($conn,"SELECT * from objects WHERE name LIKE '%$search%' LIMIT 9") or die("could not search! Oops, Panpan database might be hacked");
                $count=mysqli_num_rows($query);
                while($row=mysqli_fetch_assoc($query)){
                $object_id=$row["object_id"];
                $object_name=$row["name"];
                $object_description=$row["description"];
                $object_up=$row["up"];
                $object_down=$row["down"];
                $object_views=$row["views"];

                $output=$object_name;
                $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
                echo $result;
                }
            }
        }   
        else{
             // print out the like query
            while($row=mysqli_fetch_assoc($query)){
            $object_id=$row["object_id"];
            $object_name=$row["name"];
            $object_description=$row["description"];
            $object_up=$row["up"];
            $object_down=$row["down"];
            $object_views=$row["views"];

            $output=$object_name;
            $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
            echo $result;
            }
        }
    }
    else{
          // print out the match query
        while($row=mysqli_fetch_assoc($query)){
            $object_id=$row["object_id"];
            $object_name=$row["name"];
            $object_description=$row["description"];
            $object_up=$row["up"];
            $object_down=$row["down"];
            $object_views=$row["views"];

            $output=$object_name;
            $result= "<a class='anchor_tag_s' href='"."object.php?id=".$object_id."'><div>".$output."</div></a>";
            echo $result;
        }
    }

}


?>

I'd suggest absolutely not searching directly on key-up. 我建议绝对不要直接在键盘上搜索。 That's incredibly inefficient and very resource intense. 这非常低效且资源非常紧张。 I'd suggest setting a timer for say, one second. 我建议设置一个计时器,比如说一秒钟。 Then, on key-up, you refresh that timer to one second. 然后,在按键时,您将该计时器刷新一秒钟。 If it ever reaches 0, then you search. 如果它达到0, 那么你搜索。 Something like this: 像这样的东西:

timer = 1000;
started = false;

$("input[type=text]").keyup( function(){
    window.timer = 1000;

    if(window.started == false) queueTimer();
    window.started = true;
});

function queueTimer(){
    if(window.timer <= 0){
        //Timer has run out, business logic here!
        searchq();
        window.started = false;
    } else {
        window.timer -= 100;

        setTimeout(queueTimer, 100);
    }
}

Set the timer to whatever you want, in milliseconds. 将计时器设置为您想要的任何值,以毫秒为单位。 I recommend one second, but you can lower it as you see fit. 我推荐一秒钟,但你可以根据需要降低它。

Check the fiddle here: 检查这里的小提琴:

https://jsfiddle.net/kbyy2m6u/ https://jsfiddle.net/kbyy2m6u/

What the code is doing 代码在做什么

Right off the bat, we set our timer to 1000 milliseconds and started to false . 马上,我们将计时器设置为1000毫秒并startedfalse

The keyup inline-function is resetting the timer to 1000 milliseconds, queues the timer, and sets started to true . keyup内联函数将定时器复位至1000毫秒,排队计时器,并设置startedtrue We set it to true so the queueTimer() function does not get queued multiple times (which would negate the entire point of queueing instant searches). 我们将其设置为true因此queueTimer()函数不会多次排队(这将取消排队即时搜索的整个点)。 Once it's set to true , it will not run queueTimer() until the timer runs out and a search is actually conducted. 一旦设置为true ,它将不会运行queueTimer()直到计时器用完并且实际进行搜索。

queueTimer() is the bread and butter here. queueTimer()是这里的面包和黄油。 If the timer has not run out yet, it will simply decrease the timer variable by 100 milliseconds, and call itself to be ran again in 100 milliseconds (looping until the timer runs out). 如果计时器还没有用完,它只会将timer变量减少100毫秒,并调用自己在100毫秒内再次运行(循环直到计时器用完)。 Once it runs out, it executes your searchq() and sets started to false again so the instant search can run once more. 一旦它运行,它执行你的searchq()并设置startedfalse再这样即时搜索可以运行一次。

In plain English, every time someone types in a search box it will wait one second before doing an instant-search. 用简单的英语,每当有人在搜索框中输入时,它会等待一秒钟才能进行即时搜索。 The timer will reset on key-up, even after the queue is started, so that while typing a person will not be searching. 即使在队列启动后,计时器也会在按键时重置,这样在键入一个人时就不会进行搜索。 This prevents multiple erroneous searches by making sure a user has paused for a bit to actually conduct a search. 这可以通过确保用户暂停实际进行搜索来防止多次错误搜索。

Now a word of caution 现在谨慎一点

Google is a giant - they make magical things happen on the web. 谷歌是一个巨人 - 他们在网络上发生了神奇的事情。 Things like an instant search are great in theory, but for those of us who aren't able to throw billions of dollars at R&D like Google, it's something that's not typically worth the headache. 像即时搜索这样的东西在理论上是很棒的,但对于我们这些无法像谷歌那样在研发上投入数十亿美元的人来说,这通常不值得头疼。

Every single call you make to the database is taxing. 您对数据库进行的每次通话都会产生负担。 If you've got other functionality on your web application that hits the DB and now you're adding an instant search, be prepared for some pretty interesting side-effects. 如果您的Web应用程序上有其他功能可以访问数据库,现在您正在添加即时搜索,请为一些非常有趣的副作用做好准备。 They may even be unforeseen side effects on totally separate databases due to data locks on your MySQL Engine . 由于MySQL引擎上的数据锁定,它们甚至可能对完全独立的数据库造成无法预料的副作用。 This is one reason why Data Locking is so prevalent in a lot of Magento installs. 这就是数据锁定在许多Magento安装中如此普遍的原因之一。

I'm sure you know what you're doing, but I want to make sure anyone who sees this question will realize the trade-off to something like instant search. 我相信你知道你在做什么,但我想确保看到这个问题的人能够实现像即时搜索这样的权衡。

You should consider two things: debounce your calls and abort any previous ajax requests. 你应该考虑两件事:去除你的电话并中止任何以前的ajax请求。

For the first one, there are lots of information on debounce/throttle techniques. 对于第一个,有很多关于去抖/油门技术的信息。 Basically you have to assure that you do not execute more than one function call ( searchq ) in a certain period of time. 基本上,您必须确保在一段时间内不执行多个函数调用( searchq )。 Thus, if the user types abc , it won't trigger 3 different searches for a , ab and abc . 因此,如果用户键入abc ,则不会触发对aababc 3次不同搜索。 Instead, it will trigger a single search for the whole result ( abc ). 相反,它将触发单个搜索整个结果( abc )。

Another thing you should consider is aborting unresolved ajax calls. 您应该考虑的另一件事是中止未解决的ajax调用。
Since you're relying on ajax calls to show results, and since ajax calls are... well... asynchronous, you shouldn't actually have multiple simultaneous ajax calls. 因为你依靠ajax调用来显示结果,并且由于ajax调用是......好...异步,你实际上不应该同时有多个ajax调用。 Take the following example: 请看以下示例:

  1. the user types ab 用户输入ab
  2. the searchq function is executed for the first character ( a ) 对第一个字符执行searchq函数( a
  3. the searchq function is executed for the new sequence ( ab ) 执行searchq函数的新序列( ab
  4. the ajax promise for the second call gets resolved 第二次调用的ajax承诺得到解决
  5. the ajax promise for the first call gets resolved 第一次调用的ajax承诺得到解决
  6. the user sees the result for the wrong input 用户看到错误输入的结果

To avoid this issue, you should abort - or ignore at least - any other ajax call that is in progress by the time you fire a new one. 为了避免这个问题,你应该中止 - 或者至少忽略 - 在你发起一个新的时候正在进行的任何其他ajax调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM