25.12.2011, 09:17
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
я говорил о data как об аргументе для перехода от функции к объекту. Потому что этот data будет странно смотреться в твоей реализации.
Сообщение от FINoM
|
Что ты имеешь в виду? data — это функция и, по совместительству, — объект.
|
по сути, у тебя функция, потому что вызывается так:
wait(function() {...})...
а у меня объект, потому что вызывается так:
new AsyncChain(...).add(function() {...})...
|
|
25.12.2011, 20:15
|
|
Новичок
|
|
Регистрация: 05.09.2010
Сообщений: 2,298
|
|
x-yuri, я тебя не понимаю.
Сообщение от x-yuri
|
по сути, у тебя функция, потому что вызывается так:
|
Сообщение от x-yuri
|
а у меня объект, потому что вызывается так:
|
Причем здесь это? Ты хотел передавать некий объект через всю цепочку, не используя аргументов в функции-звене. Я так и сделал.
|
|
26.12.2011, 13:53
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
нет, я хотел хранить состояние в цепочке. А передача объекта через всю цепочку - это решение в контексте твоего подхода.
И нету смысла делать так, как я сказал. Ты должен сам видеть необходимость. Если не видишь - делай по-своему.
Еще раз, я тебе хотел сказать одну вещь: ты слишком упрощаешь, когда используешь функции, вместо объектов. Все. Необходимость хранить состояние - это всего лишь аргумент в пользу использования объектов.
|
|
26.12.2011, 20:57
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
давайте все же расмотрим конкретный пример: ресайз картинок на клиенте с помощью flash.
Например, есть форма изменения юзерпика, в которой пользователь выбирает изображение. В результате появляется диалоговое окно, в котором пользователь указывает, как надо обрезать это изображение. Нажимает "Ok" диалогового окна, потом кнопку "Сохранить" формы. Получается что-то типа:
var chain = new AsyncChain();
$('jsChangeUserpicForm-eUserpicField').addEvent('change', // input type="file"
chain
.setOptions({
onFailure: function() {
// показать пользователю сообщение об ошибке
this.fireEvent('complete');
},
onComplete: function() {
new Button('jsChangeUserpicForm-eAttachFileLink')
.enable();
$('jsChangeUserpicForm-eAttachFileThrobber')
.addClass('sInvisible');
this.state('cropResizeUserpicDialog')
&& this.state('cropResizeUserpicDialog').close();
}
})
.add(function() {
new Button('jsChangeUserpicForm-eAttachFileLink')
.disable();
$('jsChangeUserpicForm-eAttachFileThrobber')
.removeClass('sInvisible');
})
.add(function(NEXT) {
sendForm({
form: 'jsChangeUserpicForm',
url: '/upload/userpic',
onSuccess: function(userpicURL) {
chain.state('userpicURL', userpicURL);
NEXT();
},
onFailure: function() {
chain.fireEvent('failure');
}
});
})
.add(function(NEXT) {
this.state('cropResizeUserpicDialog', new CropResizeUserpicDialog({
userpicURL: chain.state('userpicURL'),
dstWidth: 100,
dstHeight: 100,
onOk: function(cropX, cropY, cropWidth, cropHeight, resizeWidth,
resizeHeight) {
this.state({
cropX: cropX,
cropY: cropY,
cropWidth: cropWidth,
cropHeight: cropHeight,
resizeWidth: resizeWidth,
resizeHeight: resizeHeight
});
NEXT();
},
onCancel: function() {
chain.fireEvent('complete');
}
}));
})
.add(function() {
this.state('cropResize', new CropResize());
this.state('cropResize').load(this.state('userpicURL'), {
onSuccess: function() {
NEXT();
},
onFailure: function() {
chain.fireEvent('failure');
}
});
})
.add(function() {
this.state('cropResize')
.crop(
this.state('cropX'),
this.state('cropY'),
this.state('cropWidth'),
this.state('cropHeight')
)
.resize(
this.state('resizeWidth'), this.state('resizeHeight'),
)
.save({
url: '/upload/thumb',
onSuccess: function() {
NEXT();
},
onFailure: function() {
chain.fireEvent('failure');
}
});
})
.add(function() {
if ($('jsChangeUserpicForm-eUserpicURLField')) {
var eUserpicURLField = $('jsChangeUserpicForm-eUserpicURLField');
} else {
var eUserpicURLField = new Element({
id: 'jsChangeUserpicForm-eUserpicField',
type: 'hidden'
});
eUserpicURLField.inject('jsChangeUserpicForm');
}
eUserpicURLField.name = 'userpic-url';
eUserpicURLField.value = this.state('userpicURL');
$('jsChangeUserpicForm-eUserpicThumb').src = this.state('userpicURL');
});
);
дополнительные преимущества:
1) возможность научить addEvent запускать цепочки в ответ на событие;
2) предоставление интерфейса для работы с состоянием позволяет повторно использовать цепочку - для этого надо перед запуском цепочки очистить состояние; если пользователь сам придумывает, где хранить состояние, нету возможности его очистить;
3) возможность организовать аналогию блока finally из try-catch (событие complete).
p.s. это я называю конкретным примером. Наличие кода необязательно в общем-то, но в твоем примере с гипотетическим парсером непонятно даже какие действия составляют цепочку, не говоря уже о данных, составляющих состояние цепочки. Т.е. не хватает важной информации.
Последний раз редактировалось x-yuri, 26.12.2011 в 21:03.
|
|
27.12.2011, 00:04
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
Сообщение от FINoM
|
Пытаюсь разобраться в backbonejs, смотрю примеры, понимаю как они работают, но не понимаю главного: зачем они так работают и как строить хваленные масштабные приложения с помощью сабжа. Может есть какие-нибудь статьи или книга "для чайников"?
|
попробовал привлечь DjDiablo к этому вопросу в этой теме
|
|
27.12.2011, 01:39
|
|
Новичок
|
|
Регистрация: 05.09.2010
Сообщений: 2,298
|
|
Сообщение от x-yuri
|
1) возможность научить addEvent запускать цепочки в ответ на событие;
|
Это круто. Только пока не понимаю, как грамотно и удобно возвращать и вызывать функцию. Наверно, идея гозара с методом, запускающим цепочку пойдет.
Сообщение от x-yuri
|
onFailure
|
Тоже круто, только
Сообщение от x-yuri
|
// показать пользователю сообщение об ошибке
this.fireEvent('co mplete');
|
вот этого я не понял. Не смотря на опечатку (вместо 'complete' наверно должно быть 'failure') чем является this? Это контекст события?
Сообщение от x-yuri
|
onComplete:
|
Зачем оно? Можно ведь в последнюю функцию запихать.
Сообщение от x-yuri
|
предоставление интерфейса для работы с состоянием позволяет повторно использовать цепочку - для этого надо перед запуском цепочки очистить состояние; если пользователь сам придумывает, где хранить состояние, нету возможности его очистить;
|
Ну только сейчас догнал, где оно может примениться.
Сообщение от x-yuri
|
непонятно даже какие действия составляют цепочку
|
Как же не понятно? Там ведь простые функции.
Сообщение от x-yuri
|
не говоря уже о данных, составляющих состояние цепочки
|
В примере состояния просто-напросто не нужны. Функции выполняются строго по-порядку, используя какую-то переменную из предыдущего вызова, если таковая есть. Того, что я предложил в начале достаточно для 90% задач, использующих асинхронные очереди.
|
|
27.12.2011, 16:36
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
Сообщение от FINoM
|
Это круто. Только пока не понимаю, как грамотно и удобно возвращать и вызывать функцию. Наверно, идея гозара с методом, запускающим цепочку пойдет.
|
какую функцию? Метод, запускающий цепочку, вызывает addEvent
Сообщение от FINoM
|
вот этого я не понял. Не смотря на опечатку (вместо 'complete' наверно должно быть 'failure') чем является this? Это контекст события?
|
там должно быть именно complete. Цепочка не знает, когда должны происходить эти события. Это задача использующего кода. Это же событие запускается если пользователь передумал, нажал "Отмена" в диалоге. this обычно указывает на объект-источник события, в частности в данном случае.
Сообщение от FINoM
|
Зачем оно? Можно ведь в последнюю функцию запихать.
|
и как ты отмену собираешься обрабатывать? Напиши свою версию этого кода
Сообщение от FINoM
|
Ну только сейчас догнал, где оно может примениться.
|
я это применение придумал, когда писал этот пример. Добавление состояния ничего не усложняет, и как видим ему нашлось применение.
Сообщение от FINoM
|
Как же не понятно? Там ведь простые функции.
|
Простые ничего полезного не делающие, ничего не обозначающие функции. У тебя абстрактный пример.
Сообщение от FINoM
|
В примере состояния просто-напросто не нужны. Функции выполняются строго по-порядку, используя какую-то переменную из предыдущего вызова, если таковая есть. Того, что я предложил в начале достаточно для 90% задач, использующих асинхронные очереди.
|
Что нужно в примере непонятно. Он может показать, как пользоваться твоей функцией. Но по нему нельзя сказать, достаточно ли функционала у твоей функции. Задача либо абстрактная, либо недостаточно подробно описана.
Сообщение от FINoM
|
Того, что я предложил в начале достаточно для 90% задач, использующих асинхронные очереди.
|
ну раз ты так говоришь, тогда я действительно зря усложняю А что ты будешь делать, когда ты столкнешься с этими 10% задач? Будешь добавлять костыли к своему решению? Или перепишешь его со всем использующим кодом?
|
|
27.12.2011, 17:15
|
|
Новичок
|
|
Регистрация: 05.09.2010
Сообщений: 2,298
|
|
Сообщение от x-yuri
|
какую функцию? Метод, запускающий цепочку, вызывает addEvent
|
Обычный запуск цепочки:
chain.add(...).add(...).run()
Запуск по событию:
doc.addEventListener(event, chain.add(...).add(...).run);
В первом случае мы сами вызываем функцию, во втором, передаем функцию для запуска после отлова события.
Сообщение от x-yuri
|
this обычно указывает на объект-источник события, в частности в данном случае.
|
Не понял фразы.
Сообщение от x-yuri
|
и как ты отмену собираешься обрабатывать? Напиши свою версию этого кода
|
Вместо
chain.setOptions({
onComplete: function(){blabla()}
}).add(function first(){}).add(function second(){})
писать
chain.add(function first(){}).add(function second(){}).add(function() blabla()})
Сообщение от x-yuri
|
я это применение придумал, когда писал этот пример. Добавление состояния ничего не усложняет, и как видим ему нашлось применение.
|
А где хранить состояние? В замкнутом объекте? В смысле, чтоб пользователь не имел к нему доступа напрямую.
Сообщение от x-yuri
|
Простые ничего полезного не делающие, ничего не обозначающие функции. У тебя абстрактный пример.
|
Блин, чем тебе пример не нравится. Или ты предлагаешь заменить функции на существующие? Какая разница, если суть от этого не изменится? Давай так: ты работал с нодой?
Сообщение от x-yuri
|
Что нужно в примере непонятно. Он может показать, как пользоваться твоей функцией. Но по нему нельзя сказать, достаточно ли функционала у твоей функции. Задача либо абстрактная, либо недостаточно подробно описана.
|
То же самое. Задача описана для тех, кто знает, что такое серверный асинхронный JS.
Сообщение от x-yuri
|
А что ты будешь делать, когда ты столкнешься с этими 10% задач? Будешь добавлять костыли к своему решению? Или перепишешь его со всем использующим кодом?
|
Нет. Буду пользоваться тем, что есть. По крайней мере, в твоем примере, я не увидел того, что не решалось бы моим способом.
|
|
27.12.2011, 19:07
|
|
|
|
Регистрация: 27.12.2008
Сообщений: 4,201
|
|
Сообщение от FINoM
|
Обычный запуск цепочки:
chain.add(...).add(...).run()
Запуск по событию:
doc.addEventListener(event, chain.add(...).add(...).run);
В первом случае мы сами вызываем функцию, во втором, передаем функцию для запуска после отлова события.
|
зачем, если можно научить addEvent работе с цепочками? Передаем ей цепочку, а она сама вызывает run.
Сообщение от x-yuri
|
this обычно указывает на объект-источник события, в частности в данном случае.
|
Сообщение от FINoM
|
Не понял фразы.
|
что в js, что в mootools, this в обработчике указывает на объект, который сгенерировал событие. Моя реализация следует этому соглашению. И jquery тоже.
Сообщение от FINoM
|
Вместо
chain.setOptions({
onComplete: function(){blabla()}
}).add(function first(){}).add(function second(){})
писать
chain.add(function first(){}).add(function second(){}).add(function() blabla()})
|
да, удобнее приводить такие вот схематические примеры, которые скрывают недостатки. Должно было быть так:
chain.setOptions({
onComplete: function() {
// выполняем завершающие действия
}
}).add(function() {
this.fireEvent('complete');
}).add(function() {
// делаем что-то еще
}).add(function() {
// делаем что-то еще
});
chain
.add(function() {
runNext(true);
})
.add(function(ignore) {
if ( ! ignore) {
// делаем что-то еще
}
runNext(ignore);
})
.add(function(ignore) {
if ( ! ignore) {
// делаем что-то еще
}
runNext(ignore);
})
.add(function(ignore) {
if (ignore) {
// выполняем завершающие действия
}
});
Сообщение от FINoM
|
А где хранить состояние? В замкнутом объекте? В смысле, чтоб пользователь не имел к нему доступа напрямую.
|
если пользователь что-то делает напрямую, либо он знает, что делает, либо ССЗБ
Сообщение от FINoM
|
Блин, чем тебе пример не нравится. Или ты предлагаешь заменить функции на существующие? Какая разница, если суть от этого не изменится? Давай так: ты работал с нодой?
|
посмотри на пример выше и сравни со своим. У тебя не видно, что твой подход приводит к куче ненужного кода. С нодой работал, но не сильно. Собственно, и что? У нас тут в теме пользователь ноды, который вообще не видит проблемы. Приведи хотя бы какой-нибудь реальный пример, в котором видно, какие выполняются действия и какие передаются данные, а лучше самый сложный, если ты не хочешь ничего скрывать.
Сообщение от FINoM
|
То же самое. Задача описана для тех, кто знает, что такое серверный асинхронный JS.
|
задача не описана, описана проблема. По проблеме нельзя судить о том, достаточно функциональности у твоей функции или нет.
Сообщение от FINoM
|
Нет. Буду пользоваться тем, что есть. По крайней мере, в твоем примере, я не увидел того, что не решалось бы моим способом.
|
хорошо, давай посмотрим на то, что есть и на то, что будет:
$('userpic').addEvent('change', // input type="file"
function() {
chain
.add(function(runNext) {
$('attach-file').addClass('disabled');
$('attach-file-throbber')
.removeClass('invisible');
sendForm({
form: 'change-userpic-form',
url: '/upload/userpic',
onSuccess: function(userpicURL) {
runNext(false, userpicURL);
},
onFailure: function() {
runNext(true);
}
});
})
.add(function(runNext, failure, userpicURL) {
if (failure) {
runNext(true);
}
var cropResizeUserpicDialog = new CropResizeUserpicDialog({
userpicURL: userpicURL,
dstWidth: 100,
dstHeight: 100,
onOk: function(cropX, cropY, cropWidth, cropHeight, resizeWidth, resizeHeight) {
runNext(false, false, userpicURL, cropResizeUserpicDialog, cropX, cropY, cropWidth, cropHeight, resizeWidth, resizeHeight);
},
onCancel: function() {
runNext(true);
}
}));
})
.add(function(runNext, ignore, failure, userpicURL, cropResizeUserpicDialog, cropX, cropY, cropWidth, cropHeight, resizeWidth, resizeHeight) {
if (ignore) {
runNext(true);
}
if (failure) {
runNext(false, true);
}
var cropResize = new CropResize();
cropResize.load(userpicURL, {
onSuccess: function() {
runNext(false, false, userpicURL, cropResizeUserpicDialog, cropX, cropY, cropWidth, cropHeight, resizeWidth, resizeHeight, cropResize);
},
onFailure: function() {
runNext(false, true);
}
});
})
.add(function(runNext, ignore, failure, userpicURL, cropResizeUserpicDialog, cropX, cropY, cropWidth, cropHeight, resizeWidth, resizeHeight, cropResize) {
if (ignore) {
runNext(true);
}
if (failure) {
runNext(false, true);
}
cropResize
.crop(cropX, cropY, cropWidth, cropHeight)
.resize(resizeWidth, resizeHeight)
.save({
url: '/upload/thumb',
onSuccess: function() {
runNext(false, false, userpicURL, cropResizeUserpicDialog);
},
onFailure: function() {
runNext(true);
}
});
})
.add(function(failure, userpicURL, cropResizeUserpicDialog) {
if (failure) {
// показать пользователю сообщение об ошибке
} else {
if ($('userpic-url')) {
var usrpicUrl = $('userpic-url');
} else {
var usrpicUrl = new Element({
id: 'userpic-url',
type: 'hidden'
});
usrpicUrl.inject('change-userpic-form');
}
usrpicUrl.name = 'userpic-url';
usrpicUrl.value = userpicURL;
$('eUserpicThumb').src = userpicURL;
}
$('attach-file').removeClass('disabled');
$('attach-file-throbber')
.addClass('invisible');
cropResizeUserpicDialog
&& cropResizeUserpicDialog.close();
});
}
);
p.s. ты либо специально скрываешь недостатки, либо... не специально. И даже если мое решение требуется в исключительных случаях... чем твое решение лучше существующих для ноды?
Последний раз редактировалось x-yuri, 27.12.2011 в 19:38.
|
|
27.12.2011, 19:49
|
|
Новичок
|
|
Регистрация: 05.09.2010
Сообщений: 2,298
|
|
Сообщение от x-yuri
|
зачем, если можно научить addEvent работе с цепочками? Передаем ей цепочку, а она сама вызывает run.
|
Ну а если мы хотим вызвать цепочку, то получится так: ...add().add().add()() ← видишь?
Сообщение от x-yuri
|
this в обработчике указывает на объект, который сгенерировал событие. Моя реализация следует этому соглашению.
|
Ты же хочешь использовать всякие вещи, типа this.state в функциях. Получается, что в обработчиках (oncomplete, onfailure) this будет элементом из события (в случае прямого запуска это будет window), а в функциях из цепочки this будет являться объектом chain?
Сообщение от x-yuri
|
Должно было быть так
|
Кто определяет как должно быть?
Сообщение от x-yuri
|
Приведи хотя бы какой-нибудь реальный пример, в котором видно, какие выполняются действия и какие передаются даныне.
|
Блин, привел же.
Сообщение от x-yuri
|
У нас тут в теме пользователь ноды, который вообще не видит проблемы.
|
Значит для него вложенные вызовы — это нормально.
Сообщение от x-yuri
|
задача не описана, описана проблема.
|
Описана задача сделать этот код более плоским.
Сообщение от x-yuri
|
чем твое решение лучше существующих для ноды?
|
Каких именно? Приведи хотя-бы одно.
|
|
|
|