Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Мой синхронный костыль (https://javascript.ru/forum/project/24115-mojj-sinkhronnyjj-kostyl.html)

x-yuri 25.12.2011 09:17

я говорил о data как об аргументе для перехода от функции к объекту. Потому что этот data будет странно смотреться в твоей реализации.

Цитата:

Сообщение от FINoM
Что ты имеешь в виду? data — это функция и, по совместительству, — объект.

по сути, у тебя функция, потому что вызывается так:
wait(function() {...})...

а у меня объект, потому что вызывается так:
new AsyncChain(...).add(function() {...})...

FINoM 25.12.2011 20:15

x-yuri, я тебя не понимаю.
Цитата:

Сообщение от x-yuri
по сути, у тебя функция, потому что вызывается так:

Цитата:

Сообщение от x-yuri
а у меня объект, потому что вызывается так:

Причем здесь это? Ты хотел передавать некий объект через всю цепочку, не используя аргументов в функции-звене. Я так и сделал.

x-yuri 26.12.2011 13:53

нет, я хотел хранить состояние в цепочке. А передача объекта через всю цепочку - это решение в контексте твоего подхода.

И нету смысла делать так, как я сказал. Ты должен сам видеть необходимость. Если не видишь - делай по-своему.

Еще раз, я тебе хотел сказать одну вещь: ты слишком упрощаешь, когда используешь функции, вместо объектов. Все. Необходимость хранить состояние - это всего лишь аргумент в пользу использования объектов.

x-yuri 26.12.2011 20:57

давайте все же расмотрим конкретный пример: ресайз картинок на клиенте с помощью 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 27.12.2011 00:04

Цитата:

Сообщение от FINoM
Пытаюсь разобраться в backbonejs, смотрю примеры, понимаю как они работают, но не понимаю главного: зачем они так работают и как строить хваленные масштабные приложения с помощью сабжа. Может есть какие-нибудь статьи или книга "для чайников"?

попробовал привлечь DjDiablo к этому вопросу в этой теме

FINoM 27.12.2011 01:39

Цитата:

Сообщение от 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% задач, использующих асинхронные очереди.

x-yuri 27.12.2011 16:36

Цитата:

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

какую функцию? Метод, запускающий цепочку, вызывает addEvent

Цитата:

Сообщение от FINoM
вот этого я не понял. Не смотря на опечатку (вместо 'complete' наверно должно быть 'failure') чем является this? Это контекст события?

там должно быть именно complete. Цепочка не знает, когда должны происходить эти события. Это задача использующего кода. Это же событие запускается если пользователь передумал, нажал "Отмена" в диалоге. this обычно указывает на объект-источник события, в частности в данном случае.

Цитата:

Сообщение от FINoM
Зачем оно? Можно ведь в последнюю функцию запихать.

и как ты отмену собираешься обрабатывать? Напиши свою версию этого кода

Цитата:

Сообщение от FINoM
Ну только сейчас догнал, где оно может примениться.

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

Цитата:

Сообщение от FINoM
Как же не понятно? Там ведь простые функции.

Простые ничего полезного не делающие, ничего не обозначающие функции. У тебя абстрактный пример.

Цитата:

Сообщение от FINoM
В примере состояния просто-напросто не нужны. Функции выполняются строго по-порядку, используя какую-то переменную из предыдущего вызова, если таковая есть. Того, что я предложил в начале достаточно для 90% задач, использующих асинхронные очереди.

Что нужно в примере непонятно. Он может показать, как пользоваться твоей функцией. Но по нему нельзя сказать, достаточно ли функционала у твоей функции. Задача либо абстрактная, либо недостаточно подробно описана.

Цитата:

Сообщение от FINoM
Того, что я предложил в начале достаточно для 90% задач, использующих асинхронные очереди.

ну раз ты так говоришь, тогда я действительно зря усложняю :) А что ты будешь делать, когда ты столкнешься с этими 10% задач? Будешь добавлять костыли к своему решению? Или перепишешь его со всем использующим кодом?

FINoM 27.12.2011 17:15

Цитата:

Сообщение от 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% задач? Будешь добавлять костыли к своему решению? Или перепишешь его со всем использующим кодом?

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

x-yuri 27.12.2011 19:07

Цитата:

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

FINoM 27.12.2011 19:49

Цитата:

Сообщение от 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
чем твое решение лучше существующих для ноды?

Каких именно? Приведи хотя-бы одно.


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