13 хаков для разработчиков: девхаки и лайфхаки -3

- такой же как Forbes, только лучше.

image
Я пишу программы уже больше 15-ти лет, из них 11 — профессионально. Т.е. получаю за это деньги. Через меня прошло десятки коммерческих проектов разной величины. Вспоминая себя того, совсем юного девелопера, я понимаю насколько разнится мышление у матерого разраба и юнца. Насколько развита интиуция и понимание после десяти лет разработки разноплановых проектов и только вышедшего из универа парня. Который, в то время, и курсовую левой рукой напишет, и дипломную, и даже авторегистратор для сайта знакомств.

В те годы, 10 лет назад, я думал скорее о хорошем тоне программирования, комментариях, наследовании и паттернах. О заученных постулатах Страуструпа и Кнута. К слову последний, до сих пор загорается яркой лампой в голове, со своей оптимизацией программ. Это «O(N^2) или O(NlogN)» невольно думается, когда выводишь 7 записей по дням недели.

А стоило прочитать серию для экспертов С++, того же Александреску или Скотта Мейрса, так мозги вскипали как бабушкин чайник. Вот и приходилось мне тогда, приступая к реальной задаче, создавать абстракции над абстракциями, оптимизировать неоптимизируемуемое, объявлять константы и константые геттеры-сеттеры. Обязательно создавать конструкторы и деструкторы, в общем и целом 90% времени заниматься оформлением кода, чтобы все было как у в учебнике.
При этом программа выводила ничего. А заказчик то ждал…

Оглядываясь на то время, и наблюдая как дела обстоят сейчас, я решил собрать небольшой шорт лист из костылей, хаков, обходных путей (workarrounds) и прочих нетривиальных ходов, о которых начинающий разработчик может не задумываться. Да впрочем не только он.

В качестве эпиграфа


Не, ну это же классика кунг-фу:

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

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

На вопрос обиженных учеников: «а как ее добиться?», ты говоришь: «никогда не делайте то-то».
Оригинал

Еще немного философии


Между тем как видит программу пользователь и как видит ее разработчик — пропасть. Мы видим коды, архитектуру, хитросплетение классов и функций. Человек же видит кнопки, поля
ввода и картинки. Нередко, пользователь получает результат, который совсем не ожидает. Нередко, пользователю выдается ошибка, от которой тот начинает нервничать и ерзать на стуле. А нам всего-то строчку поправить.

Так вот главное связующее звено между вешей программой и пользователем — это его восприятие. То есть то, что видит пользователь — такие у него и ощущения от вашей программы. Казалось бы очевидные вещи, но есть другая сторона медали. Это восприятие программиста. Об этом говорилось много раз, говорилось о правильном построение архитектуры, разделении обязанностей между классами, асинхронности и производительности.

Но редко кто заикался о восприятии самого пользователя. Того парня, который никогда не увидит SOLID-принципов за приложением с одной кнопкой. Того человека, который и платит деньги, и во многом благодаря которому эта программа [еще] существует.

Большая часть моего опыта — это фриланс. Или работа на заказ, с прямым общением с заказчиком. В этом плане работа в большой IT-компании в корне отличается восприятием процесса разработки, хотя бы по причине отсутствия контакта с конечным пользователем. Сеньоры в таких компаниях могут неделями и месяцами выстраивать многослойную архитектуру, обобщать интерфейсы и дженерики, и в 99% случаев заниматься оверинженирингом.

Во фрилансе же — ситуация ровно противоположная. Нет, тут не царит повсеместный говнокод… хотя кому я вру.

Часто именно он самый, да кодинг на скорость — вот что получается от хотелок заказчика. Т.к. бюджет лимитирован, а на код, код-ревью, юнит-тесты, QA всем плевать.

Есть исключения, но если мы говорим об условном Джонни из Флориды с 1000$ в кармане, и Вами на другом конце провода в качестве исполнителя — то ситуация именно такая. Не GoF паттерны с юнит-тестами ждет Джонни за свои кровные, а удовлетворить свой business need.

Ок. Бог с ним с фрилансом. Вариант №2 — крупная компания и дедлайн. Очень распространенная ситуация. Сдавать новую версию нужно вчера, а приложение в один из 100 запусков выдает NPE. Что делать? Сгустились тучи над компанией, все сбежались один баг фиксить. И так и этак, а у кого-то дай и упадет. Не до хорошего уже. Костыль один, второй, третий… и о чудо! — Не падает!

Это было лирическое отступление. А теперь ближе к списку хаков.

Суть


Для начала, давайте отойдем от кода и обратим внимание на две главные вещи, которые
влияют на восприятие пользователя при работе с программой — это А) Картинка Б) Время.
Повторюсь, именно Картинка — то, что видит пользователь, и Время — сколько времени он работает, ждет, загружает, и так далее. Звуки, вибрации, может быть даже запахи — тоже возможно. Но в конечном счете взаимодействие сводится к этим двум.

Скажу более, картинка и время — это не только про пользователя. Это и про заказчика, и про менеджера, и про процесс разработки в целом. Именно поэтому — я всегда начинаю разработку с UI. Всегда. Чем раньше покажешь красивые интерфейс заказчику, тем сильнее будет впечатление, и тем быстрее выстроятся доверительные отношения между вами. Будь я тим-лидом, фрилансером или шараварщиком — первое, что я делал — это Картинка (User Interface). Нет, даже не так. Very Cool User Interface. И плевать на внутренности и все остальное на этом этапе.

На кону время. Время разработки и установление доверительных отношений.

А теперь наконец о хаках. Тех самых хитростях, которые могут помочь вам справиться с дедлайном, пофиксить неуловимый баг или «сделать видимость как должно быть». Вы должно быть их знаете, но только в другом контексте.

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

Девхаки. Хаки при разработке.


1. Таймер


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

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

Это может быть вообще за пределами программы или происходить в закрытом модуле. Проходит 5 секунд — и обновить, закрыть, прибить, ваш вариант.

Вариация на тему: отдельный поток с функцией Sleep().

Пример из реального проекта:
Плагин для браузера, после загрузки страницы, должен был изменять ее содержимое. Стандартный обработчик loaded или ready() срабатывал слишком рано, и не все элементы и фреймы подгружались к этому моменту. Поэтому пользователю показывалось белое окно поверх сайта, которое через 10 секунд пропадало. Чего было вполне достаточно, чтобы весь контент загрузился и плагин изменил содержимое всей страницы.

2. Watchdog


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

Процессом может быть как само приложение, внутри которого сидит «собака», так и внешний процесс. Так что, если вы вдруг услышите от другого разработчика — «А давай на него собаку пустим!» — знайте, речь идет о watchdog.

Примеры из реальных проектов:

  1. Программа не должна была быть закрыта или прибита из таск менеджера. Она запускала watchdog, который каждую милисекунду проверял список запущенных процессов, и в случае чего, запускал основной процесс заново.
  2. Программа делала backup'ы файлов. Второй Watchdog процесс мониторил создание бекапов в определенной папке и отправлял уведомления на сервер.

3. Callback


Очень часто, особенно в больших проектах, существует проблема передать данные из одного объекта в другой. Для этих целей можно использовать и Dependedncy Injection, однако не везде подходит. Речь же сейчас идет о девхаках. (Злобный читатель может назвать их грязыми хаками, но это не так. Некоторые из них довольно чистые).

В C# для этого отлично подходят обертки над делегатами Action, Func, В других языках — (указатели на) функции. До них будет просто добраться, если сделать статическими полями.

Пример из реального проекта:

В отдельном usercontrol пользователь нажимает checkbox. В основном окне, нужно проверить, чтобы отмеченных флажков было не больше 5.

На C#:

class MyControl
{
    public static Func<bool> IsMoreThan5;

    void OnChecked(CheckBox checkBox)
    {
           if (IsMoreThan5(checkBox))
           {
                    Error("Можно отметить только 5 флажков!");
                    checkBox.IsChecked = false;
           }
     }
}

class MainWindoow
{
       public MainWindoow()
       {
             MyControl.IsMoreThan5 = MoreThan5;   // Задаем Callback
       }

        private bool MoreThan5()
        {
             int total = 0;
             foreach (var checkBox in AllCheckboxes)
             {
                  if (++total > 5)
                      return true;
              }
             return false;
        }
}

4. Визуальный эффект


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

В реальном проекте:

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

Можно еще так
Если процедура слишкооом дооолгая, то можно программно отключать монитор или открывать CD-ROM. Пока пользователь оклемается от неожиданности можно сэкономить несколько десятков секунд.

5. Скрыть/показать


Не столько интересен сам хак, сколько проблемы, которые он помог решить:

1. Однажды мне попался проект десктопного приложения, где фронт-енд был написан на HTML/JS и хостился в CEFSharp браузере.

Так как приложение было под Windows, а мне гораздо удобнее работать с WPF, чем HTML, то было сделано следующее:

Когда нужно было открыть экран с HTML-UI — показывался CEFSharp браузер с ним. А уже новые окна, которые были сделаны на WPF — показывались вместо браузера. Сам CEFSharp — прятался. Заказчик остался доволен.

2. Многие приложения устроены по принципу: разные меню, фон и основной placeholder — место где изменяется содержимое. Придумано множество фреймворков, где нужно создать 3 разных файла, 2 проекта, и в 5 местах зарегистрировать экраны, чтобы их можно было переключать. В WPF приложениях я поступаю проще — храню все экраны (контролы) в свернутом виде в основном окне, и по запросу один показываю, а остальные прячу. Никаких оверхедов и фреймворков. Все просто.

6. Отодвинуть окно за пределы экрана


Пример из реального проекта:

Нужно было запускать приложение, но чтобы пользователь его не видел. Malware? — спросите вы. Нет, не оно. В окне приложения грузился другой сайт и его содержимое нужно было проанализировать в фоне. Делаем что-то вроде

window.SetPosition(-10000, 0);   

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

7. Exe wrapping


Миллионы готовых утилит и программ лежит в свободном доступе. Возможно не нужно ничего писать? Зашить утилиту в ресурсы своего приложения, распаковать при старте и запустить. (Опционально)

Есть два момента: 1) антивирусу это может не понравиться 2) проверяйте лицензию программы.

Пример из реального проекта:
ffmpeg — универсальная утилита. Везде, где фото и видео обработка в проекте требовала больших усилий — запускался ffmpeg.

8. Точный случай (Exact case)


Когда-то на хабре пробегал перевод про геймдев, в котором разработчик отчаянно пытался пофиксить баг. Если не ошибаюсь, его персонаж постоянно проваливался за стену именно в одном месте. Тогда он решил сделать фикс таким образом: когда объект находится именно в этой координате, то сдвигать его в сторону. Profit! Если же у вас случается exception при каком-то определенном условии — логично его проверять и корректировать данные.

9. Хуки


Это из области системного программирования. Хуки можно повесить глобально на почти любые события в ОС. Будь-то события клавиатуры, мыши, запуск процессов итд.

Пример из проекта:

Писал я как-то кейлоггер… Нет, была и реальная необходимость. Например в приложении для проверки зрения, мышь нужно было останавливать возле определенного символа. Девхак Хук может быть полезен, если например мы имеем закрытый модуль, защищенный
от изменений, а он себя не правильно ведет при нажатии клавиш или кнопок мыши. Хук на клаву делает workarround.

Далее речь пойдет меньше о технических, больше о жизненных хаках. О лайфхаках.

Лайфхаки


10. Чистый проект


Это из области психологии. Чем больше визуального шума, разных сущностей и раздражителей в нашем поле зрения — тем сильнее это давит на мозг, и как следствие, решить задачу становится невыносимо сложно. Если вы столкнулись с нетривиальной задачей и никак не можете ее решить, выделить ее в отдельный проект, в отдельном солюшене. Чтобы ничего вас не отвлекало от одной единственной задачи, которую надо решить. Входные данные можно сгенерировать, захардкодить итд. А ядро решения, должно родится именно там, где весь ваш фокус. Правило психологии: куда внимание — туда и энергия.

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

Преимущества очевидны:

a) Время компиляции не всего солюшена, а только небольшого модуля в x5, x10 раз быстрее
б) Внимание сосредоточено там, где должно быть.

11. Аутсорс подзадачи


А задумывались ли вы когда-нибудь, зачем писать код, если его может написать кто-нибудь другой? Мы как разработчики, часто думаем, что именно мы должны делать реализацию всего проекта От и До.

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

В реальном проекте:

Например у меня не было времени разбираться с модулем, в котором полно матана. В итоге за небольшую сумму отдал задачу написать модуль другому фрилансеру и по завершении спокойно прицепил его во всю систему.

Давая фрилансеру подробную спецификацию и понимания четко что нужно сделать — положительный результат гарантирован.

Более того, только начав формировать задачу, а затем получая заявки от разработчиков, уже на этом этапе можно найти решение, не тратя деньги.

12. Звонок другу


Как вариант — вопрос коллеге. Мы программисты погружены в свой мир, который и является рамками нашего мышления.

Хотя бы проговорить задачу и проблему, а на том конце услышать «Угу» — уже разгрузит голову и подведет ближе к решению.

Неплохо работает в комбинации с лайфхаком «Чистый проект».

13. Декомпиляция


Где может пригодиться:

— Узнать как работает код закрытого модуля (в первую очередь)
— Проверить секьюрность программы, в том числе обфусцированной
— Взять кусок кода себе (в последнюю очередь)

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

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (34):

  1. AllexIn
    /#10561868

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

    Бить. Бить ногами. Долго и с чувством. Бить с осознанием, что бьеш ответственного за то, что весь веб превращается в одник кусок… И ладно бы просто говнокодил по тихому, но нет, надо рассказать об этом как о хорошем примере использования таймеров.

    • AllexIn
      /#10561872

      Вообще, на середине прочтения статьи я уже хотел написать что мой пост не актуален, потому что оказывается это шуточная статья в духе «Вредные советы», и я просто не сразу это понял… Но нет, это же подается на серьезных щах!

      • wdforge
        /#10562138 / +1

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

        • Free_ze
          /#10562894

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

          Либо делай хорошо, либо не делай вовсе. Юзер не виноват в том, что у вас денег нет.

        • andrikeev
          /#10563874

          Суть претензии как раз в том, что эти "маленькие приложения со скромным бюджетом" составляют огромную долю и как результат "весь веб превращается в кусок.."

  2. dabar347
    /#10561900

    10 секунд? Серьезно?

  3. izzholtik
    /#10561936

    Ну такое себе. Слишком уныло для вброса.

  4. Marwin
    /#10561956

    ну не знаю, мне статья автора — как родная… за свои аналогичные 15 лет кодинга на шарпе в той или иной форме делались почти все перечисленные пункты. Ситуации разные бывают...

  5. PrincessYork
    /#10562140

    Вот читаю я статею, и двоякие мысли в голове: «Это гениально!» и «Какая боль, черт подери, ну почему все решается настолько через ж**у».
    Так или иначе, что-то да почерпнуть тут можно, как мне кажется.
    Но белое окно поверх сайта это конечно перебор… Никогда так не делайте :)

  6. VMichael
    /#10562194

    Хорошая статья до девхаков.
    Выкинуть из нее девхаки, все правильно и в тему.
    А вот хаки (вполне себе имеющие право на жизнь) к первой части статьи ну никак не клеятся.
    Т.е. вводная часть одна статья, девхаки — другая статья.

  7. wsf
    /#10562292

    В этом плане работа в большой IT-компании в корне отличается восприятием процесса разработки, хотя бы по причине отсутствия контакта с конечным пользователем. Сеньоры в таких компаниях могут неделями и месяцами выстраивать многослойную архитектуру, обобщать интерфейсы и дженерики, и в 99% случаев заниматься оверинженирингом.

    — Так мне Паваротти не понравился, картавит, в ноты не попадает…
    — Вы были на концерте Паваротти?
    — Нет, мне Рабинович по телефону напел.

    В крупных прод решениях порой попадается такой ад, что описанные «хаки» на их фоне — это мелкая шалость.

  8. JuraZ
    /#10562316

    Ошибочка в 3-ем примере — IsMoreThan5(checkBox)

  9. saw_tooth
    /#10562342

    1. Таймер

    Тю, а я уж думал таймер в таком варианте.
    thread.sleep(10) //создаст впечатление, что программа работает

    • Zo0m3R
      /#10562884

      Еще надо винт нагрузить, активно че-то читать начать или, надежней, писать. «Во как шуршит, считает что-то!»

  10. XpySt
    /#10562492

    Нужно учить людей писать быстро и хорошо, а вместо этого автор просто перечислил набор своих совершенно конкретных костылей к совершенно конкретным проектам. Это же не универсально. Да, вероятно, многие (и я сам, не скрою) писали что-то такое, но ведь мы понимаем что это не есть правильно и в начале статьи автор выразил правильную мысль «главное — гармония», нужно строить архитектуру гармонично, не пилить велосипедов и будет вам счастье и скорость разработки. Не нужно зацикливаться на одном инструменте наивно полагая, что только на нём можно писать что-то хорошее, нужно быть открытым к новым знаниям, программист — это человек который умеет именно программировать (может грамотно составить алгоритм решения задачи), и выразить этот алгоритм именно в правильной форме (с помощью правильных иснтрументов), а так получается просто писатель костылей, что ли…

  11. andreysmind
    /#10562544

    Теги: лайфхаки, какахи, девхаки.
    Хотел запостировать картинку с двумя чуваками, где у мужика слева нормальный запас слов, а у мужика справа только «лайфхак», то там есть мат, который могут увидеть детичитатели хабра.

  12. aquamakc
    /#10562564

    Набор вредных советов. Белый экран, логика в контролах, скрытие экранных форм…

  13. vlreshet
    /#10562752

    Частично комментаторы правы, и это всё вредные советы, которые не надо использовать. Но! Мы ведь не в идеальном мире, и иногда бывают ситуации когда не до паттернов и архитектуры, а лишь бы работало. И в такие моменты знание какого-нибудь грязного костыля может пригодится. ИМХО, пост имеет место в реальном мире

    • aquamakc
      /#10562800

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

      • vlreshet
        /#10562972

        Никто и не спорит. Костыли это зло. Но когда что-то сломалось, и надо пофиксить за 10 минут, иначе клиент потеряет дохрена прибыли (ну, как пример) — то бывает приходится и костылять. Тут главное разделять костыли написанные от неопытности и незнания, и костыли которые написанные от временной необходимости

        • aquamakc
          /#10563000

          Быстро пофиксить баг — да, но не забыть про этап рефакторинга. Технический долг, если его не отдавать имеет свойство накапливаться. Этот гнойник может прорвать в самый ответственный момент и тогда банальным костылём уже не исправишь беду.

  14. F0iL
    /#10562822

    Сеньоры в таких компаниях могут неделями и месяцами выстраивать многослойную архитектуру, обобщать интерфейсы и дженерики, и в 99% случаев заниматься оверинженирингом.
    Во фрилансе же — ситуация ровно противоположная.

    Мне кажется, все это объясняется гораздо проще, и оверинжиниринг упомянут тут вообще не к месту — во фрилансе у автора заказы вида «херак-херак, лишь бы заработало, а дальше хоть потоп», а у разработчиков в энтерпрайзных компаниях задача не только «чтобы заработало», но и «чтобы через 5 лет в этом коде можно было легко разобраться, а внесение в него изменений и расширение функционала не напоминало хождение по минному полю».

    Стандартный обработчик loaded или ready() срабатывал слишком рано, и не все элементы и фреймы подгружались к этому моменту. Поэтому пользователю показывалось белое окно поверх сайта, которое через 10 секунд пропадало. Чего было вполне достаточно, чтобы весь контент загрузился и плагин изменил содержимое всей страницы.

    Подобный хак имеет один фатальный недостаток. В один прекрасный день, когда сложатся фаза луны, загрузка каналов и оборудования, и разные другие незначительные факторы, элементы страницы загрузятся не за 1-2-5-10 секунд, а за 11, и все, приплыли.

    • vlreshet
      /#10562980

      Подобный хак имеет один фатальный недостаток. В один прекрасный день, когда сложатся фаза луны, загрузка каналов и оборудования, и разные другие незначительные факторы, элементы страницы загрузятся не за 1-2-5-10 секунд, а за 11, и все, приплыли.
      А во всё остальное время юзер может смотреть лишние 5 секунд на белый экран, хотя всё уже прогрузилось. На то это и костыль. Был бы это 100% рабочий способ без подводных камней — это уже было бы хорошее решение)

  15. AleksandrKu
    /#10562928

    image

    • F0iL
      /#10563020

      велосипед должен быть с квадратными колесами!

  16. SergejSh
    /#10563152

    Как бы там ни было идея о том что не всегда нужно все делать Правильно!
    А иногда неправильно сделать Правильнее вполне жизнеспособна.

    • aquamakc
      /#10563260

      Зачем делать правильно, если можно сделать неправильно?
      image

    • F0iL
      /#10564008

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

  17. Я думал, я «применяю идейно плохие решения возникающих проблем». Ан нет. Я просто следую половине советов из этой статьи

  18. VagabundSketch
    /#10563888

    Не проще ли всякие ffmpeg в зависимости добавить, чем в само приложение встраивать? Мы так с Вами к Docker придём.

  19. Mishootk
    /#10563890

    1. Программа не должна была быть закрыта или прибита из таск менеджера. Она запускала watchdog, который каждую милисекунду проверял список запущенных процессов, и в случае чего, запускал основной процесс заново.

    Я отстал от жизни, или ОС для ширпотреба (сужу по наличию таскменеджера открытого пользователю) позволяет честно работать с такими мелкими квантами?

    • F0iL
      /#10564002

      Cкорее всего проверка каждую миллисекунду там была только по мнению автора, а по факту в коде было что-то типа usleep(1000) и оно срабатывало с гораздо большим интервалом (но никто это не проверял и не замечал, само собой).

  20. AnonymC
    /#10563892

    белое окно поверх сайта, которое через 10 секунд пропадало.

    Если это плагин для браузера — нельзя ли использовать метод из jQuery — .load()?
    $(document).load(function(e){
    }) ;

    срабатывает когда весь контент загружен и все запросы завершены.

  21. ReakTiVe-007
    /#10563894

    Заголовок спойлера
    Если процедура слишкооом дооолгая, то можно программно отключать монитор или открывать CD-ROM. Пока пользователь оклемается от неожиданности можно сэкономить несколько десятков секунд.