8

Отмена асинхронной обратной передачи

13 августа 2009 года

Пишу скорее для себя и г-на Зимина, ибо других программистов здесь нет.

Ситуация: есть форма создания нового объекта, например пользователя. Нажимаем на кнопку submit, отправляется запрос серверу, создается новый пользователь, форма закрывается. Нажимаем быстро два раза на кнопку submit — отправляется запрос серверу, но форма еще не закрылась и при повторном нажатии отправился еще один запрос. Как итог, были созданы два одинаковых пользователя. Нажимаем очень быстро 10 раз на кнопку submit, получаем 10 одинаковых пользователей в базе.

  

Вообщем-то, ларчик просто открывается — наша форма находится внутри контрола UpdatePanel, который свою очередь управляется ScriptManager'ом. Контроллировать ход асинхронной передачи можно с помощью класса Sys.Webforms.PageRequestManager — он позволяет использовать набор определенных событий, на которые можно "повесить" пользовательские функции, определяющие нужное поведение запросов. Более подробно можно прочитать здесь.

Конкретно в нашем случае мы делаем следующее:

1) Определяем js-функцию:

function CheckStatus(sender, args)
{
  var prm = Sys.WebForms.PageRequestManager.getInstance(); 
  if (prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'InsertBtn') {   
     args.set_cancel(true);    
  }
}

Данная функция проверяет, выполняется ли уже асинхронная передача и кто её инициировал. В нашем случае, если запрос на создание пользователя уже был отправлен (т.е. событие было вызвано нажатием кнопки с идентификатором "InsertBtn"), то мы не должны допустить отрпавку повторного запроса!

2) Для события initializeRequest класса Sys.Webforms.PageRequestManager определяем обработчик — функцию CheckStatus.Также мы определяем обработчик события load Sys.Application:

Sys.Application.add_load(ApplicationLoadHandler)

function ApplicationLoadHandler(sender, args)
{
     Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(CheckStatus);
}

3) Финальный аккорд- сохраняем это все в отдельном файле и указываем его в качестве скрипта для нашего ScriptManager'а:

   
<asp:ScriptManager ID="smp" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/_common/js/postback.js" />
    </Scripts>   
</asp:ScriptManager>    

Метки: , , , ,

8 комментариев к записи «Отмена асинхронной обратной передачи»

  1. Хасянов Ирек,

    А на клиентской части я бы рекомендовал кнопке отправки формы ставить свойство disabled.

  2. mkp,

    Деактивировать кнопку на клиентской части сразу после нажатия — это первое, что приходит в голову. Однако проблема в том, что поиск элемента по DOM-дереву требует существенных (в данном случае) временных затрат. Проще говоря — пользователь успеет повторно нажать на кнопку прежде, чем она будет сделана disabled.

  3. vigor,

    Это что за дерево такое у тебя? Может назначить id кнопке, по нему быстро поиск в дереве должен пройти. Так быстро, что пользователь не успеет два раза кликнуть)

  4. Наталья,

    Ребята, а на начало поста вы не обиделись? Про 2-х программистов, один из которых работает не у нас :).

  5. mkp,

    vigor,
    [b]Может назначить id кнопке, по нему быстро поиск в дереве должен пройти.[/b]

    cм. мой ответ Иреку.

    Еще раз — я не стал бы изобретать велосипед, если бы можно было просто деактивировать кнопку по id с помощью javascript.

  6. Роман Корнеев,

    mkp,
    [cite]Однако проблема в том, что поиск элемента по DOM-дереву требует существенных (в данном случае) временных затрат. Проще говоря — пользователь успеет повторно нажать на кнопку прежде, чем она будет сделана disabled.[/cite]

    Дело даже не в том, что поиск по DOM-дереву очень ресурсоемкий, а в том, что перед тем как произойдет сабмит, вызывается клиентское событие onclick у кнопки, в котором вы хотите установить в true атрибут disabled. Если вы это сделаете, то никакого сабмита не произайдет (даже самого первого), так как клиентский скрипт выполняется перед сабмитом, а у задисеблинной кнопки сабмит вызвать не получится. Так что здесь два варианта: первый – тот, который предложил Константин, второй — заменять кнопку на заранее задесейбленную кнопку, которая была невидимой до нажатия на первую.

  7. Дмитрий Котельников,

    Константин, вещь достаточно известная. Есть такой замечательный проект "муви", там подобное реализовано. Направление твоей мыслиmkp,
    [cite]Деактивировать кнопку на клиентской части сразу после нажатия — это первое, что приходит в голову. Однако проблема в том, что поиск элемента по DOM-дереву требует существенных (в данном случае) временных затрат. Проще говоря — пользователь успеет повторно нажать на кнопку прежде, чем она будет сделана disabled.[/cite] верно. Так что респект "ТехМувиБогу" Воробьеву Коле!

  8. mkp,

    Сейчас пришла идея — а что, если деактивацию кнопки сделать через некоторое время после нажатия, чтобы успел произойти сабмит? Т.е. на onclick кнопки повесить что-то типа:
    [i]"window.setTimeout(‘$(‘#buttonID’).disabled=true’,1000); document.forms[0].submit()"[/i] ?

Оставить комментарий