JavaScript: не выгружать документ, пока HttpRequest не будет завершен

Мне нужно убедиться, что когда кто-то перезагружает или закрывает мою страницу, их прогресс сохраняется. Чтобы сохранить прогресс, я делаю POST через XMLHttpRequest(), отправляя данные на сервер. Я запускаю эту функцию saveData() внутри события window.onbeforeunload.

Проблема в том, что saveData() выполняет некоторые вычисления, а затем вызывает sendData(content), чтобы, наконец, фактически отправить данные. И если данные, которые я обрабатываю и отправляю, велики (> 100 КБ), окно, кажется, закрывается до того, как все данные попадут на сервер. (Я отправляю изображение, и иногда я получаю только половину на другой стороне)

HTTP-запрос является синхронным, xhr.open("POST", url, false), но, похоже, это не работает.

Итак, мой вопрос: как я могу предотвратить завершение функции onbeforeunload, ДО xhr.readyState == 4? Как заставить его ждать этого события?

Ох, и setTimeout() не получится. Окно закроется до того, как наступит «Время».

Это не сработает:

if (!jQuery) {
    setTimeout(attachCode, 100);
    return;
} else {
    // continue and close
}

Спасибо за любое понимание!

PS: это не должно быть «плохой практикой», так как это занимает всего несколько секунд, и я не хочу, чтобы пользователю приходилось сохранять вручную. Кроме того, сохранение, пока он работает (например, до того, как он закроет окно), замедлит работу приложения, поэтому я не могу этого сделать.

[EDIT] В качестве временной меры я прибегнул к использованию этот трюк для автоматического сохранения данных, если пользователь решит остаться на странице. Однако мне бы очень хотелось, чтобы не нужно было использовать какие-либо предупреждающие сообщения. :(


person Spectraljump    schedule 25.02.2011    source источник


Ответы (2)


Запрещение браузеру нормального поведения обычно является плохой идеей. И с onbeforeunload вы не можете приостановить выгрузку, вы можете только дать пользователю знать, что он / она покидает страницу, а затем позволить им решить, покидать страницу или нет - и, таким образом, данные не сохраняются.

Итак, в вашем случае я бы предложил черновик автосохранения, как вы видите в документах Google + предупреждение, когда пользователь покидает страницу с несохраненными данными.

person Tokimon    schedule 25.02.2011
comment
Я согласен. Но я действительно ненавижу, когда сайты останавливают пользователя всплывающим окном и просят его сохранить. В моем случае он никак не хотел бы сохраниться, и это занимает 3 секунды. Хотя получается хлопотно... - person Spectraljump; 25.02.2011

На самом деле можно тормозить браузер перед выгрузкой. Проблема заключается в том, как JS обрабатывает запросы ajax.

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

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

Имейте в виду, что я использовал для этого jQuery, но это применимо к нативному JS (я думаю.. :))

/* some code above */
var request = {
    async:false,
    cache: false,
    url: this.serviceURL,
    type: 'POST',
    data: {...},
    success: function(data) {}
};
try {
    var asd = $j.ajax(request); // do the request and wait
}
catch (e) {}
/* some code below */

Надеюсь, это поможет.

person bisko    schedule 25.02.2011
comment
Спасибо за трюк с попыткой поймать. Однако кажется, что он работает для обновления, но не для закрытия. Это либо означает, что браузер (хром) принудительно закрывает вкладку независимо от сценария, либо означает, что обновление занимает больше времени, и у сценария есть время для завершения - и в этом случае это также означает, что я делаю это неправильно... Идея заключалась в том, чтобы использовать try{}catch(){} для ожидания возврата значения функцией ajax, верно? - person Spectraljump; 25.02.2011
comment
[обновление] По сути, если я вставлю alert() после var asd = jQuery.ajax(request);, он никогда не сработает, если я закрою вкладку. Это работает, хотя для обновления. - person Spectraljump; 25.02.2011