Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   ProcessMessages (https://javascript.ru/forum/misc/1609-processmessages.html)

Autosof 23.08.2008 18:12

ProcessMessages
 
Вопрос к более знающим и понимающим.

Как в javascript реализовать что-то наподобие Application.ProcessMessages в Dephi. На мой взгляд, использование такого механизма очень удобно.

Например, используя Ajax нужно отлавливать события завершения процесса передачи данных, а значит нужно объявлять дополнительные функции-обработчики. Если таких запросов много то и функций-обработчиков, как правило, тоже много. А чем больше программного кода, тем сложнее в него впоследствии вносить изменения и вообще сопровождать. Это правило, к сожалению, никто не отменял. Конечно, можно выполнять запросы асинхронно, но останавливать работу всей страницы пока не будут получены нужные данные – недружественно (как минимум) по отношению к пользователю.

Используя Application.ProcessMessages в Delphi вроде бы как и ждёшь в одном месте программы, но в тоже время и работу приложения не останавливаешь. Очень хочется сделать тоже самое и в javascript.

Вопрос как? Есть идеи?

Андрей Параничев 23.08.2008 18:38

Надо сразу сказать, что JavaScript - строго однопоточный язык.

Использование ProcessMessages в длинных циклах можно реализовать через отложенное выполнение итераций (через setTimeout), но только для того, чтоб можно было совершать действия практически параллельно с итерациями цикла, т.е. чистая эмуляция. Я не очень понимаю, как это поможет в случае Ajax.

Вы хотите избавиться от асинхронности когда в Ajax приложениях? Даже если это чем-то оправдано, реализовать такое будет сложновато. Тем более, что при правильном дизайне Ajax приложения обработчики не будут создавать проблем.

В любом случае, я не понял, что вы конкретно хотите сделать. Напишите пример на псевдо-коде.

Autosof 23.08.2008 19:04

Ну например вот так:

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) можна прекращать.

Kolyaj 23.08.2008 20:01

Autosof, ну делайте синхронные запросы к серверу, будет так, как вы хотите. Ну разве что браузер будет зависать, но это ведь мелочь по сравнению с описанием коллбэка.

З.Ы. Не надо стили работы с одним языком переносить на другой, это никогда ни к чему хорошему не приводило. Хотите изучать JavaScript -- изучайте, хотите дальше писать на Delphi -- пишите, а из их смеси ничего хорошего не получится (если вообще чего-нибудь получится).

Gvozd 23.08.2008 20:55

чесно, не доконца понимаю, что хотел бы сделать автор. возможно ему бы подошла такая форма записи:
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-ф-цию

Autosof 26.08.2008 13:30

Kolyaj:

Цитата:

Autosof, ну делайте синхронные запросы к серверу, будет так, как вы хотите.
Ну разве что браузер будет зависать, но это ведь мелочь по сравнению с
описанием коллбэка.
Ну вообщето я как раз и хотел узнать как избавиться от такого зависания...

Цитата:

Хотите изучать JavaScript -- изучайте,
хотите дальше писать на Delphi -- пишите, а из их смеси ничего хорошего не
получится (если вообще чего-нибудь получится).
Вот теперь я не понимаю. При чем тут изучение Javascript и из чего следует что меня интересует смешивание языков???

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"

Kolyaj 26.08.2008 13:37

Цитата:

Сообщение от Autosof
При чем тут изучение Javascript и из чего следует что меня интересует смешивание языков???

Принципы написания программ на разных языках разные. И изучение языка -- это, в первую очередь, изучение этих принципов (а не синтаксиса). Вы же пытаетесь принципы Delphi перенести в JavaScript. Желаемая вами схема просто невозможна в JavaScript (о чем и было сказано выше).

З.Ы. Вы лучше пишите с колбэками, потом Delphi будете вспоминать, как страшный сон.

Autosof 26.08.2008 14:28

Цитата:

Сообщение от Kolyaj (Сообщение 4991)
Желаемая вами схема просто невозможна в JavaScript (о чем и было сказано выше).

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

Цитата:

Сообщение от Kolyaj (Сообщение 4991)
З.Ы. Вы лучше пишите с колбэками, потом Delphi будете вспоминать, как страшный сон.

Delphi:
- получение результатов работы функций с местах обращения к ним - есть;
- использование событий (в том числе в виде callback) - есть;
- многопоточность - есть.

javascript:
- получение результатов работы функций с местах обращения к ним - есть;
- использование событий (в том числе в виде callback) - есть;
- многопоточность - нет (или всё таки есть?).

Javascript и Delphi - два одинаково страшных сна? :)

Андрей Параничев 26.08.2008 14:31

Autosof,
AJAX = Asynchronus JavaScript and XML. Асинхронность кода (callbacks) вы убрать полностью не сможете, не подвешивая браузер. В синхронном же варианте можно убрать подвисание одним способом - установкой колбеков.

И в JavaScript нет многопоточности. Лишь асинхронность в порядке выполнения колбеков и событий в общем потоке.

Kolyaj 26.08.2008 14:50

Цитата:

Сообщение от Autosof
Javascript и Delphi - два одинаково страшных сна?

Ну если по теме: в JavaScript функции -- объекты первого типа (их можно передавать, как параметры, в другие функции и возвращать, как результат работы функции), что в сочетании с замыканиями открывает такие возможности написания простого и лаконичного кода, который ObjectPascal'ю даже не снился.

З.Ы. За сим холивар заканчиваю.

Autosof 26.08.2008 15:00

Цитата:

Сообщение от Kolyaj (Сообщение 4999)
Ну если по теме: в JavaScript функции -- объекты первого типа

Это явно из какой-то теории. По сему вопрос - какие типы ещё бывают и чем отличаются (кроме номеров)? Ну это так, из спортивного интереса.

Цитата:

Сообщение от Kolyaj (Сообщение 4999)
... открывает такие возможности написания простого и лаконичного кода, который ObjectPascal'ю даже не снился.

Цены бы Javascript не было, если бы можно было реализовать то с чего тема начата была...
А так остается только приспосабливаться к особенностям языка.

Почемучкин 02.08.2011 06:55

Мнэээ... наткнулся тут на эту тему. И чисто для справки тем кто как и я будут читать архив, вставлю, что я пока в JavaScript не большой специалист, но в Delphi тоже можно передавать функции как параметры - по ссылке, и возвращать как результат работы. Более того, есть даже возможность описывать типы как функции. Например можно сделать массив или список функций.
И еще - почитал тут в инете, что вроде можно в JavaScript использовать window.postMessage, так что какая-никакая обработка сообщений в нем есть и соответственно это касается вопроса топикстартера. Он не про многопоточность спрашивал, а про передачу управления в очередь обработки сообщений, пока происходит ожидание - заметьте, что это именно в однопоточном приложении. И насколько я понял - если пользоваться очередью сообщений, написав свой ProcessMessages, то только в IE скрипт будет блокироваться, а в других браузерах будет работать асинхронно.

Владимир_Фомин 06.09.2014 15:57

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

Очень хотелось бы сделать, чтобы браузер не зависал при длительном вычислении, то есть чтобы рекурсивная функция длительного перебора не препятствовала другим процессам. Очень хотелось бы найти аналог 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

Цитата:

procedure recgen(k: integer; h: integer; g: integer);
var
i: integer;
j: integer;
t: integer;
q: boolean;
begin
Application.ProcessMessages;
if k = size*size + 1
then
begin
u0:=u0+1;
Form2.EditNumberOfSolutions.Text:=IntToStr(u0);
randomize;
randomfield;
for i:=1 to size*size do
if r[i]<=h
then gen[i]:=sg[i]
else gen[i]:=0;
rank(g);
end;
if (k<=size*size) and (stop=false) and (u<>g)
then
begin
randomize;
randomingnumber;
for t := 1 to size do
begin
q := true;
for j := 1 to mc[k] do
if y[t] = sg[m[k, j]]
then q := false;
if (q = true)
then
begin
sg[k] := y[t];
recgen(k+1, h, g);
end;
end;
sg[k] := 0;
end;
end;

А что является аналогом Application.ProcessMessages в javascript?

Aetae 06.09.2014 16:09

JS однопоточный язык. Нет в нём изначально никаких "других" процессов. Остановка - есть остановка всего на странице.

Современное решение: web worker. Он создаёт отдельный поток из отдельного скрипта, что общается с основным потоком сообщениями.
По старинке: дробить вычисления setTimeout'ами.

kobezzza 06.09.2014 22:27

Я как раз сейчас этим занимаюсь в рамках Collection 5.2 (в сообщение по ссылке есть ссылка на демо видео, где создано 50 потоков с разными приоритетами, каждый из которых считает 1кк итераций), где потоки реализуются с помощью прерываний (yield) функций и планировщика, т.е. модель такая же как и в потоках на одноядерных процах.

Цитата:

Современное решение: web worker.
К сожалению решение довольно однобокое, т.к. с одной стороны - это не JS, а некоторая приблуда браузера, а также очень много ограничений (в том числе на количество создаваемых потоков в рамках домена), да и сама операция создания довольно дорогая.

Aetae 06.09.2014 22:38

Цитата:

Сообщение от kobezzza (Сообщение 329270)
К сожалению решение довольно однобокое

Справедливо, тем не менее большинство обычных проблем решает.
То что делаешь ты - крутотень, но весьма специфическая.)

kobezzza 06.09.2014 22:44

Цитата:

Сообщение от Aetae (Сообщение 329274)
То что делаешь ты - крутотень, но весьма специфическая.)

На самом деле нет, как сделаю релиз, то сможешь убедится: я сделаю наглядную демку, в которой можно будет пощупать и увидеть, что всё супер просто.

// Простая синхронная итерация
$C([...]).forEach(function () {
    ...
});

// Итерация в потоке
$C([...]).forEach(function () {
    ...
}, {thread: true});


Главный плюс таких потоков, что по сути, мы остаёмся в главном потоке и у нас спокойный доступ к DOM, переменным замыкания и т.д. хотя с другой стороны это и минус - т.к. можно выстрелить себе в ногу.

К сожалению самый главный недостаток этого подхода - это необходимость поддержки генераторов, а она есть только в Хроме и ФФ, но обещали вкрутить в ИЕ12, а про Сафари я даже не знаю, надо будет посмотреть, но в ноде я уже планирую полноценное внедрение этой технологии.

Владимир_Фомин 07.09.2014 20:05

Цитата:

Сообщение от Aetae (Сообщение 329222)
По старинке: дробить вычисления setTimeout'ами.

Вот именно! Очень хотелось бы без всяких премудростей setTimeout'ами добиться того, чтобы браузер не зависал.

Но куда вставлять этот 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);

то ничего не получается, так как она просто будет через него перескакивать, корректного результата не получится.

Aetae 07.09.2014 20:24

Переписать рекурсию на циклы.

kobezzza 12.09.2014 16:53

Оставлю тут ссылку: реализация потоков в Collection 5.2

Aetae 12.09.2014 17:13

kobezzza, ну ему всё равно чтоб это использовать придётся раскрыть рекурсию.)

kobezzza 12.09.2014 17:21

Цитата:

Сообщение от Aetae (Сообщение 330269)
kobezzza, ну ему всё равно чтоб это использовать придётся раскрыть рекурсию.)

Зачем? Можно же плодить рекурсивные потоки.

function doIt(data) {
    return $C(data).forEach(function (el) {
        if (typeof el === 'object') {
            this.wait(doIt(el));
        }
    }, {thread: true});
}


Т.е. поток создающий поток и т.д.

Но переполнение стека в таком случае никто не отменял :)

Aetae 12.09.2014 17:43

kobezzza, оппа, почему-то мне после ознакомления не пришло в голову использование таким образом. Взгляд замылился об использование в итерациях.
Возможно стоит сделать рекламный пример "рекурсивное вычисление [что-то ресурсоёмкое] без подвисания браузера".
А может просто я тупой, и все и так поймут - не стал бы такого исключать.)

kobezzza 12.09.2014 17:54

Ну пока промышленное использование только в ноде возможно, т.к. из браузеров генераторы поддерживает сейчас ФФ и Хром (правда нужно включить флаг). ИЕ должен в 12-й версии научится.

Следует заметить, что всё итерационные операции Collection поддерживают потоки, а не только forEach.

$C(...).map(Math.sqrt, {thread: true, onComplete: function (result) { ... }});


Цитата:

Возможно стоит сделать рекламный пример "рекурсивное вычисление [что-то ресурсоёмкое] без подвисания браузера".
Я добавлю это в доку.


Часовой пояс GMT +3, время: 21:38.