ProcessMessages
Вопрос к более знающим и понимающим.
Как в javascript реализовать что-то наподобие Application.ProcessMessages в Dephi. На мой взгляд, использование такого механизма очень удобно. Например, используя Ajax нужно отлавливать события завершения процесса передачи данных, а значит нужно объявлять дополнительные функции-обработчики. Если таких запросов много то и функций-обработчиков, как правило, тоже много. А чем больше программного кода, тем сложнее в него впоследствии вносить изменения и вообще сопровождать. Это правило, к сожалению, никто не отменял. Конечно, можно выполнять запросы асинхронно, но останавливать работу всей страницы пока не будут получены нужные данные – недружественно (как минимум) по отношению к пользователю. Используя Application.ProcessMessages в Delphi вроде бы как и ждёшь в одном месте программы, но в тоже время и работу приложения не останавливаешь. Очень хочется сделать тоже самое и в javascript. Вопрос как? Есть идеи? |
Надо сразу сказать, что JavaScript - строго однопоточный язык.
Использование ProcessMessages в длинных циклах можно реализовать через отложенное выполнение итераций (через setTimeout), но только для того, чтоб можно было совершать действия практически параллельно с итерациями цикла, т.е. чистая эмуляция. Я не очень понимаю, как это поможет в случае Ajax. Вы хотите избавиться от асинхронности когда в Ajax приложениях? Даже если это чем-то оправдано, реализовать такое будет сложновато. Тем более, что при правильном дизайне Ajax приложения обработчики не будут создавать проблем. В любом случае, я не понял, что вы конкретно хотите сделать. Напишите пример на псевдо-коде. |
Ну например вот так:
function data_upload(name_first, name_last) { var ajax = new ajax_object() ajax.set('name_first', name_first) ajax.set('name_last', name_last) return ajax.upload() } alert('Данные ' + (data_upload('Вася', 'Пупкин') ? 'сохранены' : 'не сохранены')) Более компактная и удобная форма ипользования могла бы получиться, только и всего... Сейчас необходимо проделывать что-то вроде следующего: function data_upload(name_first, name_last, callback) { var ajax = new ajax_object() ajax.set('name_first', name_first) ajax.set('name_last', name_last) ajax.upload(callback) } function callback(ajax) { alert('Данные ' + (ajax.ok ? 'сохранены' : 'не сохранены')) } data_upload('Вася', 'Пупкин', callback) Кода намного больше, и он не всегда удобен... я бы даже сказал что неудобен всегда. P.S. Не вызывает же необходимость обращения скажем к Math описывать callback функции для этого. Результат можно получить непосредственно в местах вызова. Вот и Ajax завернуть в "обертку" такой концепции было бы очень хорошо. Тоесть без использования функций callback. Вообще говоря callback всё равно был бы. Но он был бы неявным. Единственная задача такой callback функции - опустить флаг занятости процеса ajax - тоесть просигнализировать что данные пришли и ожидание (использующее ProcessMessages) можна прекращать. |
Autosof, ну делайте синхронные запросы к серверу, будет так, как вы хотите. Ну разве что браузер будет зависать, но это ведь мелочь по сравнению с описанием коллбэка.
З.Ы. Не надо стили работы с одним языком переносить на другой, это никогда ни к чему хорошему не приводило. Хотите изучать JavaScript -- изучайте, хотите дальше писать на Delphi -- пишите, а из их смеси ничего хорошего не получится (если вообще чего-нибудь получится). |
чесно, не доконца понимаю, что хотел бы сделать автор. возможно ему бы подошла такая форма записи:
function data_upload(name_first, name_last, callback) { var ajax = new ajax_object() ajax.set('name_first', name_first) ajax.set('name_last', name_last) ajax.upload(callback) } data_upload('Вася', 'Пупкин', function(ajax){ alert('Данные ' + (ajax.ok ? 'сохранены' : 'не сохранены')) //выполняется после прихода ответа с сервера }) alert("а ответа еще нету") //выполняется независимо от Ajaxa просто в том месте, где вы передаете в функцию-обертку data_upload() колбек-функцию callback(ajax), вы передаете не имя функции, а прямым тексом ее саму. при этом в глобальной области видитмости ее нету, и после выполнения запроса она исчезает.и видно сразу, что произойдет, после запроса, а не надо искать где-то в листинге callback-ф-цию |
Kolyaj:
Цитата:
Цитата:
Gvozd: Надо так: function data_upload(name_first, name_last, callback) { var ajax = new ajax_object() ajax.set('name_first', name_first) ajax.set('name_last', name_last) ajax.upload(callback) } data_upload('Вася', 'Пупкин', function(ajax){ alert('Данные ' + (ajax.ok ? 'сохранены' : 'не сохранены')) // выполняется после прихода ответа с сервера // в первую очередь }) alert("а ответа еще нету") // выполняется после прихода ответа с сервера // во вторую очередь // а не "выполняется независимо от Ajaxa" |
Цитата:
З.Ы. Вы лучше пишите с колбэками, потом Delphi будете вспоминать, как страшный сон. |
Цитата:
И очень, кстати говоря, жаль. Цитата:
- получение результатов работы функций с местах обращения к ним - есть; - использование событий (в том числе в виде callback) - есть; - многопоточность - есть. javascript: - получение результатов работы функций с местах обращения к ним - есть; - использование событий (в том числе в виде callback) - есть; - многопоточность - нет (или всё таки есть?). Javascript и Delphi - два одинаково страшных сна? :) |
Autosof,
AJAX = Asynchronus JavaScript and XML. Асинхронность кода (callbacks) вы убрать полностью не сможете, не подвешивая браузер. В синхронном же варианте можно убрать подвисание одним способом - установкой колбеков. И в JavaScript нет многопоточности. Лишь асинхронность в порядке выполнения колбеков и событий в общем потоке. |
Цитата:
З.Ы. За сим холивар заканчиваю. |
Цитата:
Цитата:
А так остается только приспосабливаться к особенностям языка. |
Мнэээ... наткнулся тут на эту тему. И чисто для справки тем кто как и я будут читать архив, вставлю, что я пока в JavaScript не большой специалист, но в Delphi тоже можно передавать функции как параметры - по ссылке, и возвращать как результат работы. Более того, есть даже возможность описывать типы как функции. Например можно сделать массив или список функций.
И еще - почитал тут в инете, что вроде можно в JavaScript использовать window.postMessage, так что какая-никакая обработка сообщений в нем есть и соответственно это касается вопроса топикстартера. Он не про многопоточность спрашивал, а про передачу управления в очередь обработки сообщений, пока происходит ожидание - заметьте, что это именно в однопоточном приложении. И насколько я понял - если пользоваться очередью сообщений, написав свой ProcessMessages, то только в IE скрипт будет блокироваться, а в других браузерах будет работать асинхронно. |
Извините, но в выложенных примерах пока никак не разобрался, может быть, позднее разберусь, когда прочту весь учебник.
Очень хотелось бы сделать, чтобы браузер не зависал при длительном вычислении, то есть чтобы рекурсивная функция длительного перебора не препятствовала другим процессам. Очень хотелось бы найти аналог Application.ProcessMessages в javascript, который позволял бы, например, идти часам во время работы программы. Браузер Firefox зависнет на целую минуту в Судоку-онлайн для примера 3, пока программа сделает 3 миллиона переборов, а в для примера 46 зависнет на целых 2 минуты, пока программа сделает 9 миллионов переборов. Вот здесь добавил часы на страничку sudokut.htm , и если вы попробуете решить пример 3 или пример 46, то увидите, что во время работы программы часы остановятся. Скрипт программы. Подвешивает браузер вот этот код. function recfrugal(k) { var i; var j; var t; var q; if (k == 82) { u++; for (i = 1; i <= 81; i++) z[i][u] = s[i] } if ((k <= 81) && (u < 10)) { if ((w[k] == 0) || (w[k] == 1)) recfrugal(k+1); if (w[k] > 1) { for (t = 1; t <= w[k]; t++) { q = true; for (j = 1; j <= 20; j++) if (b[k][t] == s[m[k][j]]) q = false; if (q == true) { s[k] = b[k][t]; c++; recfrugal(k+1); } } s[k] = 0; c++; } } } Что конкретно надо добавить в код программы, чтобы зависание браузера во время длительных вычислений не происходило? В Паскале (Лазарусе) достаточно было для этого дописать в код функции одну только строчку Application.ProcessMessages Цитата:
|
JS однопоточный язык. Нет в нём изначально никаких "других" процессов. Остановка - есть остановка всего на странице.
Современное решение: web worker. Он создаёт отдельный поток из отдельного скрипта, что общается с основным потоком сообщениями. По старинке: дробить вычисления setTimeout'ами. |
Я как раз сейчас этим занимаюсь в рамках Collection 5.2 (в сообщение по ссылке есть ссылка на демо видео, где создано 50 потоков с разными приоритетами, каждый из которых считает 1кк итераций), где потоки реализуются с помощью прерываний (yield) функций и планировщика, т.е. модель такая же как и в потоках на одноядерных процах.
Цитата:
|
Цитата:
То что делаешь ты - крутотень, но весьма специфическая.) |
Цитата:
// Простая синхронная итерация $C([...]).forEach(function () { ... }); // Итерация в потоке $C([...]).forEach(function () { ... }, {thread: true}); Главный плюс таких потоков, что по сути, мы остаёмся в главном потоке и у нас спокойный доступ к DOM, переменным замыкания и т.д. хотя с другой стороны это и минус - т.к. можно выстрелить себе в ногу. К сожалению самый главный недостаток этого подхода - это необходимость поддержки генераторов, а она есть только в Хроме и ФФ, но обещали вкрутить в ИЕ12, а про Сафари я даже не знаю, надо будет посмотреть, но в ноде я уже планирую полноценное внедрение этой технологии. |
Цитата:
Но куда вставлять этот setTimeout, если функция, вызывающая зависание браузера, рекурсивная? function recfrugal(k) { var i; var j; var t; var q; if (k == 82) { u++; for (i = 1; i <= 81; i++) z[i][u] = s[i] } if ((k <= 81) && (u < 10)) { if ((w[k] == 0) || (w[k] == 1)) recfrugal(k+1); if (w[k] > 1) { for (t = 1; t <= w[k]; t++) { q = true; for (j = 1; j <= 20; j++) if (b[k][t] == s[m[k][j]]) q = false; if (q == true) { s[k] = b[k][t]; c++; recfrugal(k+1); } } s[k] = 0; c++; } } } Если заменить рекурсивный вызов recfrugal(k+1); вот так: setTimeout(function() { recfrugal(k+1) }, 1000); то ничего не получается, так как она просто будет через него перескакивать, корректного результата не получится. |
Переписать рекурсию на циклы.
![]() |
Оставлю тут ссылку: реализация потоков в Collection 5.2
|
kobezzza, ну ему всё равно чтоб это использовать придётся раскрыть рекурсию.)
|
Цитата:
function doIt(data) { return $C(data).forEach(function (el) { if (typeof el === 'object') { this.wait(doIt(el)); } }, {thread: true}); } Т.е. поток создающий поток и т.д. Но переполнение стека в таком случае никто не отменял :) |
kobezzza, оппа, почему-то мне после ознакомления не пришло в голову использование таким образом. Взгляд замылился об использование в итерациях.
Возможно стоит сделать рекламный пример "рекурсивное вычисление [что-то ресурсоёмкое] без подвисания браузера". А может просто я тупой, и все и так поймут - не стал бы такого исключать.) |
Ну пока промышленное использование только в ноде возможно, т.к. из браузеров генераторы поддерживает сейчас ФФ и Хром (правда нужно включить флаг). ИЕ должен в 12-й версии научится.
Следует заметить, что всё итерационные операции Collection поддерживают потоки, а не только forEach. $C(...).map(Math.sqrt, {thread: true, onComplete: function (result) { ... }}); Цитата:
|
Часовой пояс GMT +3, время: 21:38. |