Suggest в Google — никакого аякса

    Честно говоря, у меня, давно сложилось впечатление, что если клиент (в данном случае броузер) что-то должен с сервера забирать без перезагрузки страницы, то это обязательно должен быть AJAX XMLHttpRequest. Ткнул куда-то пользователь, сработало событие, сгенерировался AJAX XMLHttpRequest-запрос, пришел с сервера ответ, коллбэк обработал его и пользователь что-то увидел.

    Но есть проблема. Если пользователь быстро и часто давит на клавиши, создается некоторое количество параллельных запросов и в результате запрос, отправленный позже может отработать намного раньше более раннего запроса, что повлечет за собой вывод неправильных данных.



    Да, можно заморочаться, придумать, как гасить ранние запросы или делать очереди. Или еще как-то.

    Мне понравилось решение гугла на эту тему:

    image

    Т.е. при каждом нажатии на кнопку в поле поиска введенный текст подставляется в URL подключаемого JS-файла, на сервере в зависимости от введенных параметров файла генерируется JS-массив с данными и в конце вызывается функция отрисовки результатов.

    Никаких AJAX XMLHttpRequest -запросов, все просто и шустро.
    Поделиться публикацией

    Комментарии 94

      +80
      Вообще то это и есть AJAX :) Только реализован на script tag транспорте.
        +1
        AJAX это асинхронное взаимодействие с сервером средствами Javascript. В приведенном примере и Javascript и взаимодействие в асинхронном режиме есть.
          +6
          В таком случае завершающий «X» явно лишний :)
            +6
            а он очень часто лишний
              +2
              Сейчас для соответствующих случаев предлагают говорить не AJAX, а AJAJ (… and JSON).
                +1
                Всю жизнь любил только AJAJ :-) Жаль называл не так :-)
          –5
          Это как раз cross domain scripting, обычные аяксовые запросы и не смогли бы пойти на поддомен гугла.
          Себе как раз такой делаю, вот пример

          function sendscriptRequest(url, httpParams, callback, callbackArgsArray) {
          var currentscript = document.createElement(«script»);
          if (httpParams)
          httpParams="?rand=" + Math.random() + "&" + httpParams;
          else
          httpParams="?rand=" + Math.random();
          currentscript.ajax_readyState = false;
          currentscript.onload = scriptCallback(currentscript, callback, callbackArgsArray);
          currentscript.onreadystatechange = scriptCallback(currentscript, callback, callbackArgsArray);
          currentscript.src = url + httpParams;
          document.getElementsByTagName(«script»)[0].parentNode.appendChild(currentscript);
          }

          function scriptCallback(currentscript, callback, callbackArgsArray){
          return function() {
          if (currentscript.ajax_readyState)
          return;
          if (! currentscript.readyState || currentscript.readyState == «loaded» || currentscript.readyState == «complete») {
          currentscript.ajax_readyState = true;
          callback.apply(currentscript, callbackArgsArray)
          currentscript.parentNode.removeChild(currentscript);
          }
          }
          }
            +2
            Вот здесь хорошая реализация script tag транспорта для GET и winndow name для POST
            forum.hivext.ru/index.php?topic=4.0

            Работает в FF2+, FF3+, IE6+ IE7+, O9+ Safari3+, Chrome. В IE8 не тестировалось.
            • НЛО прилетело и опубликовало эту надпись здесь
                +19
                Да, да, бей его, мужики!!1
                • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  теоретически можно, но нужно бубен доставать… в разных браузерах разные реализации с поддоменных запросов в АЯКС
                    • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      поидее речь идет не о поддоменах, а о разных доменах google.us, google.com, google.ca…

                        0
                        речь идёт о CLIENTS1.google.com
                      +3
                      Вроде человек, всё верно говорит, чего я заминусовали-то?

                      XHR же не может выполнять кросс-доменные запросы, вместо этого применяют JSONP (http://www.jstoolbox.com/2009/03/18/chto-takoe-jsonp/)
                        0
                        Если заминусовали, значит не зря. Хотя я тоже сначала не понял. Но если подумать все очевидно.

                        Если не понял, поясняю. В заметке говориться о запросе с google.com на clients1.google.com. XHR такие выды запросов делать позволяет. Поэтому в данном случае JSONP можно и не использовать.
                        0
                        Да есть старенька библиотечка
                          0
                          Да есть старенькая библиотечка для JS+PHP
                          // JSHttpRequest v1.2. © Dmitry Koterov, 2005-01-27.
                          // forum.dklab.ru/users/DmitryKoterov/
                          Я как-то совсем про нее забыл, хотя долго ею пользовался. Недавно тоже столкнулся с проблемой обратится AJAX запросом к поддомену. Решение это как раз использовать запросы через подргузку JS массивов
                            0
                            Почему старенькая? :-) Есть же свежая версия, она и на moikrug.ru используется (как раз для кросс-доменного AJAX, в частности)…
                              0
                              Это у меня она старенькая завалялась, перешел с нее потому, что дебажить было сложно. С фаербагом окозалось все значительно проще. После это к сожалению не отслеживал изменения.
                          –5
                          AJAX это не только XMLHttpRequest :) это наше все :D обожаю эту технологию, так что не знаю, что это вы :)
                            0
                            Троли или я где-то неправ?
                            +8
                            «Если пользователь быстро и часто давит на клавиши», не обязательно посылать запрос после каждого нажатия. Ограничение на частоту запросов сделать совсем не сложно.
                              +2
                              > Но есть проблема. Если пользователь быстро и часто давит на клавиши, создается некоторое количество параллельных запросов и в результате запрос, отправленный позже может отработать намного раньше более раннего запроса, что повлечет за собой вывод неправильных данных.

                              а разве указанный подход решает эту проблему? все равно имеется вероятность более раннего окончания загрузки скрипта который запросился позже предыдущего.
                                0
                                Код через <script> выполняется синхронно… тоесть пока не выполниться один ничего другое javascript-овое выполняться не будет. На обычной странице последовательно подключённые скрипты никогда не перемешиваются и выполняются в строгой последовательности… (грузятся может и паралельно, но выполняются точно по порядку)
                                  +5
                                  уверены? при асинхронном подключении скриптов это правило неработает. покрайней мере точно не для всех браузеров. ИЕ, Хроме выполняет сразу первый ответивший. ФФ выполняет по порядку добавления. Только что протестировал.
                                    0
                                    Выложите, если не сложно, сылку на тест… Всё конечно возможно… и если вы правы для меня это будет открытием
                                    >«асинхронное подключения» звучит не правдоподобно для тега
                                    Убеждён, что в следующем коде $ будет доступен сразу после добавления:

                                    var script=document.createElement('script');
                                    script.type='text/javascript';
                                    script.src='jquery.js';
                                    document.body.appendChild(script);
                                    alert($) //ну не будет здесь ошибки ни в одном из браузеров:)
                                      +3
                                      var script=document.createElement('script');
                                      script.type='text/javascript';
                                      script.src='test-slow.js';
                                      document.getElementsByTagName('head')[0].appendChild(script);

                                      var script=document.createElement('script');
                                      script.type='text/javascript';
                                      script.src='test-fast.js';
                                      document.getElementsByTagName('head')[0].appendChild(script);

                                      в test-slow.js и test-fast.js сделайте alert('slow') и alert('slow') соот-но

                                      на ответ test-slow.js поставьте задержку в пару секунд

                                      посмотрите на результ.
                                      точно такие же тесты удалил :). проверил и снес.

                                        –1
                                        >на ответ test-slow.js поставьте задержку в пару секунд
                                        А как вы выставляли задержку?
                                        если setTimout-ом то конечно setTimout выполнится после того как загрузится следующий скрипт, а не до.
                                          +1
                                          на сервере задержку на ответ. могу вам выложить даже с учетом комментария ниже. если проанализируете, и напишите короткий обзор результатов.
                                            +2
                                            sirus я убедился что вы были правы… добавленый таким образом скрипт в одних браузерах выполняется синхронно, а в других ассинхронно тест
                                              0
                                              короткий обзор может когда-нибудь… ещё моментов накопится и напишу
                                      0
                                      Так скрипты же не подключаются больше — всего один тег script с изменяемым источником.
                                        –1
                                        ну да… если менять src, javascript также остановит выполнение пока не будет сделана загрузка и выполнен скрипт и только потом будет возможно опять изменить src… помоему будет так, но тестов не делал… может sirus выложит тест и развеет все сомнения
                                          +1
                                          Не остановит — это асинхронные запросы получаются.
                                            0
                                            Да действительно… зависит от браузера тест
                                              0
                                              Ну тут уже проблемы терминологии — относительно основного кода такая подгрузка асинхронна, код выполняется. А вот сами запросы в некоторых браузерах становятся друг за дружкой.
                                              Надо смотреть в статьях sunnybear 'а, там точно было про это.
                                          +1
                                          так вообще в разных браузерах по разному работает

                                          var script=document.createElement('script');
                                          script.type='text/javascript';
                                          script.src='test2.js';
                                          document.getElementsByTagName('head')[0].appendChild(script);

                                          script.src='test3.js';

                                          FF — сработает только test2.js
                                          ИЕ — сработает только test3.js
                                            0
                                            Так, был неправ насчёт одного тега. В коде явно видно создание тегов, глубже пока не смотрел.
                                            А вот поведение дикое какое-то… Сейчас тоже буду тестировать.
                                              0
                                              Гугл делает всё логично — сносит существующий файл (таким образом прерывается загрузка) и создаёт новый.

                                              При изменении src не происходит закачка/парсинг скрипта (FF). Помнится, были обсуждения, как «пнуть» браузер, но совсем ничего не помню… :(

                                              ИЕ же при изменении послушно ползает за каждым файлом. Если в ваш пример добавить задержку при изменении src, то это будет видно. При быстрой же смене загрузка предыдущей версии прерывается.
                                              Т.е. моё предположение могло бы работать в ИЕ =)
                                                0
                                                > Гугл делает всё логично — сносит существующий файл (таким образом прерывается загрузка) и создаёт новый.

                                                а вы проверяли что загрузка прерывается если снести скрипт который уже в процессе :)?
                                                  0
                                                  Бррр… ничего не понимаю… FF не прерывает…
                                                  Фик с Гуглом, проблема решается. А почему такое странное поведение?

                                                  var script1=document.createElement('script');
                                                  script1.type='text/javascript';
                                                  script1.src='test1.php';
                                                  document.getElementsByTagName('head')[0].appendChild(script1);

                                                  alert('a');
                                                  script1.parentNode.removeChild(script1);
                                                  alert('b');

                                                  Для скрипта ставим задержку. В итоге получаем 'a', 'b' и '1' (вывод test1.php). Удаление переменной тоже не помогает. FF цепко держится за подгружаемые файлы…
                                                  IE вот обрубает.
                                                    0
                                                    а кто его знает, вот такая вот реальность. отменить начатое низзя везде полноценно :)
                                      0
                                      В одно время пользовался библиотекой с похожим принципом- Subsys_JsHttpRequest.
                                      В ней реализована передача php переменных в js посредством script tag.
                                      Кроссбраузернасть отличная.
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                          Ну
                                            +3
                                            Упс. Ну, вообще говоря, в JsHttpRequest поддерживаются 3 транспорта: script, XMLHttpRequest, а также IFRAME — для закачки файлов аяксом на сервер. Плюс автоматом выбирается оптимальный (из тех, что поддерживает текущий браузер, в зависимости от кроссдоменности и т.д.). Но, конечно, есть и отдельный версии библиотеки, по одной на каждый вид транспорта (например, можно взять только JsHttpRequest-script.js, в которой ничего, кроме script-транспорта, нет).
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                            0
                                            У Гугла-то все круто :)

                                            А вот по поводу этого:
                                            > «Но есть проблема. Если пользователь быстро и часто давит на клавиши, создается некоторое количество параллельных запросов и в результате запрос, отправленный позже может отработать намного раньше более раннего запроса, что повлечет за собой вывод неправильных данных.»

                                            Это как так??

                                            1. Пользователь наживает «а»
                                            2. Отправляется запрос "...&query=а"
                                            3. Пользователь наживает «б»
                                            4.1. Если запрос на «а» еще не отработал — отменяется запрос на «а».
                                            4.2. Если запрос на «а» уже отработал — просто уже отображаются результаты поиска на «а».
                                            5. Отправляем запрос "...&query=аб"

                                            и так далее.

                                            В чем проблема-то? Каждый запрос отменяет предыдущий, если тот еще не отработал и одновременно содержит его в себе (как «аб» содержит «а»), да и все.
                                              –3
                                              Отправляем запрос «а».
                                              Отправляем запрос «б».
                                              Отправляем запрос «в».
                                              Запрос «б» завершился.
                                              Запрос «в» завершился.
                                              Запрос «а» завершился.
                                              В итоге имеем результат не по «абв», а по «а».
                                              Запросы ведь друг друга не убивают. Они параллельно висят.
                                                0
                                                >Запросы ведь друг друга не убивают. Они параллельно висят.
                                                Откуда такая уверенность?))
                                                  +2
                                                  Час назад проверяли.
                                                    0
                                                    Запросы отменяете, если они еще не обработаны.
                                                    Вообще объект suggest-а должен содержать некий пул и «знать», какой запрос отправлен последним.
                                                      –4
                                                      Это ясно. Но обстановка такая, что интерфейс нужно сделать срочно (2-3 часа на весь интерфейс для менеджеров). Можно сделать умно и красиво, но дольше. Либо быстро и глупо, но быстрее. В данном случае «глупый» вариант более предпочтителен.
                                                      +1
                                                      Ну это уже косяк разработчиков. То что это реализуемо, потому что сие очень даже реализуемо.
                                                    +2
                                                    Я бы поотрывал яйца Client-side разработчикам, которые для Suggest-поиска могут сделать параллельные запросы по каждой букве :)
                                                      0
                                                      Я бы тоже. Но у меня их мало осталось (разработчиков), экономлю.
                                                        0
                                                        Ну в качестве профилактики можно сначала одно — а заодно пригрозить отрыванием другого :)
                                                        0
                                                        Гугловский suggest срабатывает и по каждой букве тоже, если печатать не слишком быстро. Дело скорее в количестве запросов в единицу времени, а не в степени изменения текста запроса.
                                                          0
                                                          Ну я же здесь вот уже написал, как оно должно срабатывать. Ничего страшного, если печатается буква и она уже успела обработаться — последующий запрос ее также содержит и выдаст более новые результаты.

                                                          И по поводу Гугла — я не смотрел в деталях, но предполагаю, что у них запросы (если печатать мееееедлеееенно):
                                                          d
                                                          de
                                                          dev
                                                          deve
                                                          devel
                                                          develo
                                                          develop

                                                          а не:
                                                          d
                                                          e
                                                          v
                                                          e
                                                          l
                                                          o
                                                          p

                                                          Или нет?
                                                            0
                                                            d
                                                            de
                                                            dev
                                                            devi < — опечатка
                                                            dev
                                                            deve
                                                              0
                                                              Ну и не страшно — если медленно печатаем — успевают, естественно, открыться результаты для «devi» — ведь откуда компьютеру знать, что есть опечатка, а что нет.
                                                              Если быстро и сразу исправили — нужно сделать, чтобы следующий запрос на «dev» или «deve» отменил запрос на devi. А еще можно включить функциональность таймера и запускать запрос с маленькой, но задержкой (мы, к слову, так и делали при необходимости) — пользователь обычно успевал написать все слово поиска, не отправив ни одного запроса «посреди» слова.
                                                                0
                                                                Я об этом и говорю — можно добавить таймеры, проверялки, отменялки. А можно и проще, хоть и не элегантно. Кому как нравится и у кого сколько времени на ту или иную задачу.
                                                                  0
                                                                  Возможно, но отправка каждой буквы по отдельности — сама по себе концептуальная ошибка. То есть, таким образом, вы сами создаете себе дополнительную проблему, которую в итоге приходится решать разными костылями.

                                                                  P.S. А Suggest-поиск — сама по себе отнюдь не сложная вещь, к тому же есть готовые решения.
                                                                    0
                                                                    А отправлять по одной букве — это не моя идея. Я говорил про отправку введенных слов.
                                                                      0
                                                                      А по-моему, не совсем

                                                                      Ну как бы там ни было, тогда самая обычная отправка запросов друг за другом вполне может подойти для вашей «быстрой» задачи.

                                                                      P.S. Таймер добавить — пара минут.
                                                              0
                                                              Аааа, я думал, что «по каждой букве» значит «после каждого изменения текста», прошу прощения :)
                                                        0
                                                        Я смотрел как работет suggest box в GWT- он заранее содержит в себе
                                                        массив из слов для показа подсказки к любой букве, т.е. какую бы букву не нажал пользователь, у suggest box будут варианты подсказок и прользователю не нужно ждать
                                                        результата запроса.
                                                        Правда в дальнейшем для добавления в массив новых значений
                                                        всё-равно придётся ждать завершения запроса. Но, так как запрос работает на опережение, указанная проблема (ошибочный результат подсказки) не возникает.
                                                        0
                                                        имхо, можно было бы сделать так, чтобы не делать запрос на каждую введенную букву, ведь запрос например массива начинающегося на «абв» содержит внутри себя массив начинающийся с «абвг». Можно было бы сделать это отсеивание на стороне клиента (когда пользователь ввел допустим больше 5 букв и масси уже не очень большой).
                                                        Хотя надо наверно потестить — как быстрее и оптимальнее.
                                                          0
                                                          Ну там если ветвить функциональность — много всяких кейсов получается, очень много. Можно все очень хорошо оптимизировать в итоге. Мы занимались этим в одном из проектов, и каких-то особых концептуальных трудностей и не встретили.
                                                          +3
                                                          AJAX — это общая концепция.
                                                          XMLHttpRequest — это конкретная технология, причём никто не говорил, что единственная.
                                                          Когда XMLHttpRequest ещё не «раскручен» был и не всеми браузерами поддерживался, повсеместно использовались скрытые iframes — тоже ведь аяксом являлось.
                                                            +5
                                                            Посмотрел в википедии. Там как раз эти 3 способа и описаны («гугловский», «ифреймовский» и «иксэмэльреквестный»)
                                                            ru.wikipedia.org/wiki/AJAX
                                                              +6
                                                              На мой взгляд, после этого коммента обсуждать в посте уже нечего. :)
                                                                0
                                                                на самом деле советую почитать английскую страничку википедии en.wikipedia.org/wiki/Ajax_(programming)
                                                                там этот термин более широко трактуется, однажды чуть не сел в лужу с этим в очередной дискуссии
                                                              0
                                                              единственное, что меня лично смущает, так это кеширование… (чтобы после запроса abcd запрос abc не загружался с сервера, а подгружался из локального)
                                                              либо в этом случае браузер сам выполняет функцию кеша?..
                                                                0
                                                                сам в данный момент столкнулся с этой проблемой. К сожалению, гугление ничего не дает.
                                                                Нужно подобие Suggest'а у гугла, но в итоге нужно получить таблицу с записями. Если вводить слово быстро, то в таблице появляются дублированные записи. После «передышки» и ввода следующей буквы, все встает на свои места.
                                                                setTimeout не помогает (
                                                                  0
                                                                  ну вот. Стоило написать, как проблема вроде как решилась. Обнуление контента таблицы вызывалось в той же функции, которая вызывает «дочернюю» через setTimeout().
                                                                  А надо было делать в той, которая вызывается непосредственно в setTimeout() =)
                                                                    0
                                                                    поздравляю!)
                                                                    хотел было что-то посоветовать, но опоздал)))
                                                                +1
                                                                А я всегда считал, что метод XMLHttpRequest.abort() отменяет текущий запрос. Помоему, все браузеры такого же мнения.
                                                                  –5
                                                                  >>Но есть проблема. Если пользователь быстро и часто давит на клавиши, создается некоторое количество параллельных запросов и в результате запрос, отправленный позже может отработать намного раньше более раннего запроса, что повлечет за собой вывод неправильных данных.

                                                                  Заблокируйте кнопку при первом нажатии, и пока не придет ответ такой оставляем. И это не Аякс, а упреждающая выборка из кеша браузера. Вот пример под ROR — leopard.in.ua/2009/03/30/molnienosnoe-javascript-avtozapolnenie/
                                                                    –1
                                                                    «круто» придумано
                                                                    осталось придумать как объяснить пользователю почему кнопки туда-сюда мигают
                                                                    окей если это будет около-айтишный человек, а ведь этим юзером может быть домохозяйка
                                                                    –3
                                                                    Вы грустные вещи пишите
                                                                      0
                                                                      Есть еще вариант отправлять клиенту выборку, допустим, по первым трем буквам, а уже на клиенте отсекать по остальным введенным буквам.
                                                                      плюсы:
                                                                      1. всего один запрос на сервер
                                                                      2. фильтрация по буквам дальше третьей на клиенте происходит почти мгновенно (хорошо для опечаток)

                                                                      минусы:
                                                                      1. не подходит для больших выборок
                                                                        0
                                                                        минус 2: релевантность по разным первым буквам разная должна быть.
                                                                          0
                                                                          ну это я думаю относится к большим выборкам. Там как раз и встает вопрос релевантности.
                                                                        +4
                                                                        Этот протокол называется JSONP.
                                                                          0
                                                                          была на хабре статья, где google suggest подробно разбирали по косточкам.
                                                                            0
                                                                            хм… у них уже так довольно давно. Все берется динамическим созданием script.
                                                                            притом при частом нажатии клавиш параллельные запросы ajax можно легко поблочить или их отслеживать, просто выставлять флаг отправки запроса и получения ответа =/
                                                                              +1
                                                                              мой любимый аджакс это form + iframe
                                                                                0
                                                                                А я таким образом эту проблему решаю: habrahabr.ru/blogs/webdev/17884/

                                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                Самое читаемое