Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет mumuntu ([info]mumuntu)
@ 2010-08-05 23:00:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Я, когда пишу такое,
for( j = 0 ; j < L ; j++ ){
    var callback = function(m) {
        return function(data) {
            $tab = $(f(m, data));
            ...
        }
    } (x[j]);
    y(x[j].k, callback);
}

с одной стороны, понимаю, что это офигенно красиво, но процентов 60-70 людей в команде этот код просто не поймут, я этот прием сам обнаружил в сети, как некий паттерн.
С другой стороны, никаких других вариантов сделать асинхронную обработку сгруппированных запросов к серверу у меня сейчас нет. Как я понимаю, асинхронная обработка - сама по себе довольно сложная для понимания вещь.
Какой-то комментарий к этому коду я написал и дал ссылку на место, где этот шаблон объясняется.
Но все равно думаю, не слишком ли много зеленого чая я пью? это фундаментальная сложность, или можно как-то проще изложить?


(Добавить комментарий)


[info]dair_spb@lj
2010-08-05 15:06 (ссылка)
Чо это за язык? JavaScript?

я не понимаю эту запись: $tab = $(f(m, data));

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:10 (ссылка)
JavaScript

$(...) - это сокращение для jQuery(...)

(Ответить) (Уровень выше)


[info]alexclear@lj
2010-08-05 15:15 (ссылка)
Да, а первый доллар, который "$tab" вообще просто так, для красоты.
Коллеги им метят то, что уже является jQuery object.

(Ответить) (Уровень выше)


[info]alexclear@lj
2010-08-05 15:07 (ссылка)
Это типичный случай горя от ума, кстати.
Гггг, коллега Павел говорит, что я слишком много разговариваю сам с собой.
Я, вроде, придумал решение попроще.

(Ответить) (Ветвь дискуссии)


[info]nolar@lj
2010-08-15 09:55 (ссылка)
Надевать гарнитурку для виду как будто говоришь с кем-то? ;)

(Ответить) (Уровень выше)


[info]kincajou@lj
2010-08-05 15:08 (ссылка)
Я понял только одно: в вашем распоряжении дофига процессорного времени.

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:13 (ссылка)
Относительно.
По сравнению с боттлнеками в коде приведенный фрагмент чуть ли не образец быстродействия.

(Ответить) (Уровень выше)


[info]bopm@lj
2010-08-05 15:14 (ссылка)
А что у нас L?

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:16 (ссылка)
Количество однотипных запросов, которые нужно сбэтчить.
Я вот что придумал - нафиг мне эти запросы сериализовать через общий бэтчер, когда они все равно однотипные? Я лучше параметры сериализую и сделаю один запрос, без этой мудянки.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]bopm@lj
2010-08-05 15:20 (ссылка)
Именно. Меньше запросов - быстрее работает. Вот если бы тебе прогресс нужно было отображать. Но и тогда тоже, один запрос, куча ответов о промежуточном стейте. Ты на такой реализации очень много теряешь на установление соединения.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:23 (ссылка)
Серег, запрос что так, что так будет один.
Сейчас на сервере есть некая общая точка входа, которая принимает массив хэшей, содержащих то, что раньше было отдельными URI с тем, что раньше было параметрами.
Собственно, приведенный код - это упаковка бэтча как раз.
Просто я вот подумал, что слишком это генерализовал.

(Ответить) (Уровень выше)


[info]184467440737095@lj
2010-08-05 15:20 (ссылка)
обычно такой код используется для создания нескольких функций с "разным j".
поскольку scope для переменных в яваскрипте - это тело функции целиком,
то если сделать function(data) { $tab = $(f(x[j], data));
то все экземпляры callback получат ссылку на один и тот же j, который после выхода из цикла будет равен L-1, что вряд ли ожидает программист, который пишет такой код первый раз.

каждый вызов функции function(m) создает свой stack frame в scope chain, и каждый возвращаемый из нее объект имеет ссылку на свой экземпляр m, который был связан в момент вызова. таким образом callback становится "разным".

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

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:28 (ссылка)
Пока не объяснял, в понедельник попробую.
Проблема этого кода в том, что если заранее не знать, в чем суть, догадаться непросто (а при некоторых начальных условиях просто невозможно).

(Ответить) (Уровень выше)

(Комментарий удалён)

[info]alexclear@lj
2010-08-05 15:26 (ссылка)
http://javascript.ru/basic/closure
Объяснение будет начиная со слов "Пример ошибочного использования" и далее.
Суть идеи в том, что для того, чтобы из цикла сделать несколько замыканий с разным контекстом в зависимости от счетчика цикла, надо использовать отдельную функцию, которая получает локальное состояние, различное для разных значений счетчика цикла.
А иначе, так как связывание происходит в момент вызова коллбэка, коллбэк получал бы один и тот же контекст для всех значений счетчика.

(Ответить) (Уровень выше)


[info]ext_202704@lj
2010-08-05 15:28 (ссылка)
замыкания это нифига не так сложно как кажется.

хороший джаваскриптер их понимает ок, а плохие нахуй не нужны

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:30 (ссылка)
Где бы их еще взять-то, хороших.
Но вообще да, надо начать на собеседованиях спрашивать, а что этот код делает и зачем он такой.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ext_202704@lj
2010-08-05 15:33 (ссылка)
у меня все знали что это такое, кто не знал — разбирался.

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

(Ответить) (Уровень выше)


[info]alexclear@lj
2010-08-05 15:36 (ссылка)
Я просто некоторым образом вырвался на свободу, и боюсь, что скоро у меня весь код будет такой.
И остальным придется как-то мириться с обильным использованием замыканий.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ext_202704@lj
2010-08-05 15:40 (ссылка)
Это прекрасно, я дико поддерживаю если чо :)

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

Первая: вернуть кол-во запусков фунции f() без использования глобальной переменной для счетчика

Вторая: на вход фунции подается массив а = ['well', 'shit', 'is', 'tight'], ф-ция меняет массив таким образом, чтоб a[1]() делала alert('shit)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]184467440737095@lj
2010-08-05 16:19 (ссылка)
>вернуть кол-во запусков фунции f() без использования глобальной переменной для счетчика

в аргументе приезжает функция f, а возвращается number?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ext_202704@lj
2010-08-13 13:51 (ссылка)
нет

написать ф-цию f, потом сделать f();f();f();f(); и получить 4

(Ответить) (Уровень выше)


[info]juan_gandhi@lj
2010-08-05 16:17 (ссылка)
+1

(Ответить) (Уровень выше)


[info]ext_202704@lj
2010-08-05 15:35 (ссылка)

for( j = 0 ; j < L ; j++ ){
y(x[j].k, (function(m) {
return function(data) {
$tab = $(f(m, data));
...
}
}) (x[j]));
}

(Ответить) (Ветвь дискуссии)


[info]ext_202704@lj
2010-08-05 15:36 (ссылка)
в том смысле, что если коллбек требуется один раз, и переменная для него не нужна

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:38 (ссылка)
А, угу.
Но читается тяжелее.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ext_202704@lj
2010-08-05 15:40 (ссылка)
С непривычки да, но вообще легче :)

(Ответить) (Уровень выше)


[info]_arty@lj
2010-08-05 16:26 (ссылка)
для простоты чтения не надёжнее ли использовать итераторы и нормальные имена функций и переменных?
типа
goog.array.forEach(x, function(xItem){
addToRequest(
xItem.someProperty,
function(data) {
$tab = $(someFunction(xItem, data));
}
)
});

или как там в jQuery итераторы называются

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 16:31 (ссылка)
В проекте нормальные имена, естественно.
Но не все.
Во-первых, некоторые коллеги любят такое вот мясо, как приведено (да-да, i, L, k, S и т.п.).
Во-вторых, надо как-то остановиться и отрефакторить имена, потому что названия иных методов просто устарели.
В общем, все как обычно.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]_arty@lj
2010-08-05 16:34 (ссылка)
ааа
просто тот код, что приведён в посте, реально непонятен
если там привести в порядок имена, то станет намного проще, и вопрос по сути сведётся к «понимают ли люди замыкания»

(Ответить) (Уровень выше)


[info]max630@lj
2010-08-05 15:45 (ссылка)
что такое y?

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:47 (ссылка)
Это вызов, который заполняет массив запросов для последующей отправки на сервер.
Как только цикл заканчивается, я делаю другой вызов, который сериализует этот заполненный массив и отправляет его на сервер.

(Ответить) (Уровень выше)


[info]max630@lj
2010-08-05 15:57 (ссылка)
а матрёшка из функций - это такой способ скопировать состояние мутирующей переменой?

так не проканает?

var m = x[j];
... function (data) { ... m .... } ....

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 15:58 (ссылка)
> а матрёшка из функций - это такой способ скопировать состояние мутирующей переменой?

Именно, через скоп вспомогательной функции.

> var m = x[j];
> ... function (data) { ... m .... } ....

Не, так не работает.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]juan_gandhi@lj
2010-08-05 16:17 (ссылка)
О, новое русское слово узнал. "Скоп". Спасибо.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 16:21 (ссылка)
Я сегодня на полном серьезе спрашивал у коллег, как сказать по-русски "багфиксинг".
Сошлись на "устранении ошибок".

(Ответить) (Уровень выше)


[info]intr13@lj
2010-08-05 21:44 (ссылка)
Хмм, я не большой специалист по замыканиям в javascript, но все же любопытно, почему не работает:

> var m = x[j];
> ... function (data) { ... m .... } ....

получается javascript каждый раз не создает новую переменную "m"? А для всего блока for(...){...} имеет общий скоп описанных переменных? Или это некий другой эффект, например ленивое вычисление значения переменной "m"?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]rainman_rocks@lj
2010-08-12 10:51 (ссылка)
Кложуры в жабоскрипте - очень незамутнённые ребята. Они не забирают с собой родительские переменные индивидуально, как в питоне. Они хватают ссылку на весь родительский скоп и уносят его с собой. Поэтому когда следующая итерация попортит переменную "m", коллбэки, созданные на предыдущих, загрустят.

Так и живём.

(Ответить) (Уровень выше)


[info]juliy@lj
2010-08-05 16:08 (ссылка)
ты че программист?!

(Ответить)


[info]juan_gandhi@lj
2010-08-05 16:15 (ссылка)
Да и хрен с ними, с людями. Этот код - competitive advantage.

(Ответить)

Попроще...
[info]lionet@lj
2010-08-05 16:16 (ссылка)
var gen_cb = function(m) { return function(data) { ... } }
for(var j = 0; j < L; j++)
y(x[j].k, gen_cb(x[j]);

(Ответить) (Ветвь дискуссии)

Re: Попроще...
[info]juan_gandhi@lj
2010-08-05 16:18 (ссылка)
Имеет очевидное преимущество.

(Ответить) (Уровень выше)

Re: Попроще...
[info]alexclear@lj
2010-08-05 16:22 (ссылка)
О, отлично
Спасибо!

(Ответить) (Уровень выше)


[info]yakov_sirotkin@lj
2010-08-06 00:25 (ссылка)
А можно переименовать переменные-функции попонятней?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]lionet@lj
2010-08-06 01:20 (ссылка)
Конечно!

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yakov_sirotkin@lj
2010-08-06 01:23 (ссылка)
Это я не разрешения спрашиваю, а прошу помочь идиоту разобраться:)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]lionet@lj
2010-08-06 01:38 (ссылка)
Проблема с длинными мнемоничными идентификаторами состоит в том, что они затмевают смысл алгоритма. Но попробую:

var generate_callback_function = function(currentElement) { return function(data) { ...data + currentElement... } }
for(var currentElementIndex = 0; currentElementIndex < elementsArray.length; currentElementIndex++) {
var currentElement = elementsArray[currentElementIndex];
other_function_which_needs_a_callback(currentElement.arbitrary_key, generate_callback_function(currentElement));
}


Италиком помечены внешние к рассматриваемому алгоритму сущности.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]yakov_sirotkin@lj
2010-08-06 02:43 (ссылка)
Спасибо, так понятно!

(Ответить) (Уровень выше)


[info]fantaseour@lj
2010-08-06 02:34 (ссылка)
чтобы разобраться, есть смысл сходить по приведенной выше ссылке:
http://javascript.ru/basic/closure

(Ответить) (Уровень выше)


[info]yurri@lj
2010-08-05 16:17 (ссылка)
А это просто пример, или ты реально такие имена используешь (x, k, L, m etc.)?

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 16:19 (ссылка)
Такие имена использует --- из ---, про которого я пишу в Твиттер.
Работать ему осталось, я надеюсь, дня два.
Все имена я заменил, принципиально не хочу показывать тот код, который реально существует, мало ли.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]alexshubert@lj
2010-08-06 05:02 (ссылка)
у тебя есть твиттер?

(Ответить) (Уровень выше)


[info]phoonzang@lj
2010-08-05 16:42 (ссылка)
вопрос по синтаксису: (x[j]); к чему относится?

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 16:58 (ссылка)
Это вызов анонимной функции function(m) прямо по месту объявления.
В var callback будет положен уже результат вызова этой функции, то есть, вложенная в ее код функция.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]phoonzang@lj
2010-08-05 17:02 (ссылка)
... в которой вместо m будет уже подставлен нужный x[j], понятно

спасибо

(Ответить) (Уровень выше)


[info]ubi_que@lj
2010-08-05 17:41 (ссылка)
а я ничего не понял, но я тоже пишу код, только очень плохой. Но, во-первых, его никто никогда кроме меня не увидит, а во вторых, наступает новое средневековье.

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

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-05 17:54 (ссылка)
Скоро все влезут в продуктивные обсуждения

(Ответить) (Уровень выше)


[info]tretiy3@lj
2010-08-05 18:16 (ссылка)
тут вроде можно мапом, без замыканий, не?




x.map(function(el){y(el.k), function(data){ ... el ...}})

(Ответить)


[info]max630@lj
2010-08-06 01:19 (ссылка)
казалось бы, если в js такая хуета с переменными, это же часто должно использоваться.

(Ответить)


[info]dmzlj@lj
2010-08-06 01:36 (ссылка)
ничего себе, т.е. в случае одинарной вложенности функция не будет замыканием и контекст теряется? А есть от авторов какое-то объяснение такому поведению?

(Ответить) (Ветвь дискуссии)


[info]lionet@lj
2010-08-06 01:43 (ссылка)
Запоминается не значение j, а контекст, содержащий j. После прохода сквозь цикл все порождённые замыкания будут содержать j, который у всех будет иметь значение L.

Это проистекает из семантики захвата контекста в JS: захватывается не значение, а переменная (типа как по ссылке).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dmzlj@lj
2010-08-06 02:20 (ссылка)
Я уже на это натыкался. Это, наверняка, простое следствие из того, что все переменные --- boxed. Любая переменная --- ссылка на бокс.

(Ответить) (Уровень выше)


[info]lionet@lj
2010-08-06 01:46 (ссылка)
Кстати, если уж делать всё внутри цикла, то вот ещё вариант на тему [info]ilya-furman.ya.ru@lj:
for(var j = 0 ; j < L ; j++ ) {
function(curX) {
y(x[j],k, function(data) { $tab = $(f(curX, data)); ... })
}(x[j]);
}

(Ответить)


[info]blacklion@lj
2010-08-06 05:15 (ссылка)
Красиво.

(Ответить)


[info]randir_spb@lj
2010-08-08 13:16 (ссылка)
Ты продолжаешь меня радовать.
Я думал, что хоть немного понимаю javascript.

(Ответить)


[info]rainman_rocks@lj
2010-08-12 11:01 (ссылка)
Такого ещё не посоветовали?

Function.prototype.boundTo = function(x) {
t = this;
return function(){t.apply(x, arguments)};
}

var callback = function(data) {
$tab = $(f(this, data));
};

for( j = 0 ; j < 5 ; j++ ){

request(x[j].k, callback.boundTo(x[j]));
}

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

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-08-12 11:22 (ссылка)
О, спасибо!
Весьма!

(Ответить) (Уровень выше)


[info]nolar@lj
2010-08-15 10:00 (ссылка)
Я непонятные вещи выношу в "библиотеки", и описываю только достигаемый эффект, входы-выходы, зачем это надо, и т.п. По принципу чёрного ящика.

Внутреннее устройство - тоже, но не стараюсь чтобы поняли все 99%, а главным образом для себя, если придётся расширять/править.

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

(Ответить)


[info]yurri@lj
2010-08-16 08:54 (ссылка)
Я слоупок, но, насколько я понял, это такое "использование костылей языка в корыстных целях", правильно?

Несколько напоминает использование i >> 1 вместо i/2 в C - т.е. вроде бы и измеримый эффект есть, но читаемость приносится в жертву.

В смысле, красиво, но я бы всё-таки так не сделал, наверное.

(Ответить) (Ветвь дискуссии)


[info]yurri@lj
2010-08-16 08:58 (ссылка)
Ещё заметил, что все в комментах ставят openstache на одну строку с оператором, меня это тоже огорчает. Что наводит на мысли, что я просто отстал, и претензия выше тоже неактуальна.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]rainman_rocks@lj
2010-08-26 13:50 (ссылка)
OTBS через четыре пробела рулит.

Всех остальных - к стенке.

(Ответить) (Уровень выше)


[info]asviraspossible@lj
2010-08-27 15:29 (ссылка)
Мне казалось вот так немного понятнее...

for( j = 0 ; j < L ; j++ ){
var callback;
function() { // new scope
var currentX = x[j];
callback = function(data) {
$tab = $(f(currentX, data));
...
}
} ();
y(x[j].k, callback);
}

(Ответить)


(Анонимно)
2010-09-05 19:38 (ссылка)
это пример хуевого "нечитаемого" кода (с сомнительной работоспособностью), написанного человеком, не знакомым с "case" и принципами работы интерпретаторов. учись, сынку.

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-09-06 03:41 (ссылка)
С каким case?
С какими принципами работы интерпретаторов?
Ты с кем говорил сейчас, дяденька?
Блядь, просили же школоту в интернет не пускать...

(Ответить) (Уровень выше)


(Анонимно)
2010-09-05 19:53 (ссылка)
каменты трешь быдлокодер ? учи основы интерпретаторов, зайчик.

(Ответить) (Ветвь дискуссии)


[info]alexclear@lj
2010-09-06 03:42 (ссылка)
Бухаешь?
Ну, бухай, бухай.
Коменты я не тру, просто у тебя от синьки шары на жопу полезли, не пиши мне больше, алик, пиши на ЛОР.

(Ответить) (Уровень выше)