Модераторы: Sardar, Aliance

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Цепочка функций, последовательное выполнение (chaining) 
:(
    Опции темы
Dr.Zlo
Дата 28.1.2008, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



В больших скриптах сталкиваюсь с необходимостью разделения анимации и логики, т.е. к примеру сначала элемент нужно красиво показать\передвинуть\подсветить, потом загрузить в него инфу, после загрузки инфы задвинуть\переместить\раздвинуть элемент, и самое главное все эти действия нужно выполнять ПОСЛЕДОВАТЕЛЬНО, следующий этап начинается только после завершения предыдущего. В основном работаю с jQuery, но если кто знает решение с помощью стандартных средств js, пишите, буду только рад. 
Мне нужно решение из серии:

chain(animate1(), logic1(),animate2()...)

или

animate1().logic1().animate2()...

Сразу скажу, стандартное решение jQuery для callback функций анимации меня не устраивает, т.е.

$('#elem').animate({.....}, speed, 'linear', function(){ logic1() }) - это мне не подходит
PM MAIL   Вверх
ksnk
Дата 28.1.2008, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



А чем не устраивает стандарное решение, можно поподробнее?





--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Dr.Zlo
Дата 28.1.2008, 13:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



Стандартное решение не утсраивает тем, что в таком случае в скрипте получается каша из визуальных эффектов и логических функций, к тому же если нам надо в каждую функцию передавать различные параметры, будет вообще ужас, если к примеру после анимации anim(x) надо выполнить logic(args), затем anim2(y), затем logic2(args2)  нужно исполнять что-то вроде:

Код

function anim(x, args, y, args2){
    $(x)animate({...}, speed, 'linear', function(){
        logic(args, y, args2)
    })
}

function anim2(y, args2){
    $(y)animate({...}, speed, 'linear', function(){
        logic2(args2);
    })
}

function logic(args, y, args2){
    // делаем что-то логическое c args
    anim2(y, args2)
}

function logic2(args2){
    // делаем что-то логическое c args2
    alert('Уррра! Наши мучения закончились!!!')
}

//вызов
anim(x, args, y, args2);


Другой пример: нам надо выполнять одну и ту же функцию anim() перед выполнением logic(args) и logic2(args2), тогда вообще получается жёсткая жесть:


Код

function anim(x, callback){
    $(x)animate({...}, speed, 'linear', function(){
        eval(callback)
    })
}

function logic(args){
    // делаем что-то логическое c args
}

function logic2(args2){
    // делаем что-то логическое c args2
}

// вызов
anim(x, 'logic('+args+')')

...

// и второй раз
anim(x, 'logic2('+args2+')')


И если не дай бог в args или args2 передаётся объект, тогда вообще придётся создавать глобальные переменные, а это вообще не фэн-шуй.

Плюс ко всему было бы удобно и красиво, если бы вся анимация была в одном js файле, а вся логика в другом, а связывались бы они с помощью тех самых цепочечных вызовов функций, в таком случае было бы очень удобно менять эффекты не копаясь в логике или модифицировать логику, не разгребая эффекты.
PM MAIL   Вверх
ksnk
Дата 28.1.2008, 16:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



Дык взять и написать объект chain, который представляет собой массив функций, каждая из которых вместо callback'а вызывает chain.next()
chain.next обязан выкинуть из собственного массива первую функцию и вызвать ее.

Код

var chain=[
function (){alert('start!');chain.next()},
function (){setTimeout(chain.next,3000)},
function(){alert('Hello!');chain.next()}
]
chain.next=function(){var x=chain.shift();if(x)x();}

chain.next();

пойдет?

Это сообщение отредактировал(а) ksnk - 28.1.2008, 16:12


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Dr.Zlo
Дата 28.1.2008, 16:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



- во-первых, такой способ принципиально не отличается от  банального вызова следующей функции в конце предыдущей
- во-вторых опять же возникает проблема передачи переменных
- в-третьих здесь функции жёстко привязываются к цепи, а ведь идея состоит в том, что функции могут вызываться как цепочечно, так и по отдельности.
Нужна некая chain конструкция, для работы которой не пришлось бы залезать в функцию и прописывать ей callback вжёсткую. В данном случае основную проблему я вижу в том, чтоб определять момент, когда текущая функция цепочки закончила работу, чтоб передать управление следующей функции, просто мне мозга пока не хватает придумать, как это реализовать  smile 
PM MAIL   Вверх
ksnk
Дата 28.1.2008, 16:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



Dr.Zlo, Я не очень пока понимаю суть возражений. Напишите как бы вы хотели писать smile  Для начала - можно "описать" ту цепочку, которую я предложил - пара алертов через таймаут. 


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Dr.Zlo
Дата 28.1.2008, 17:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



ksnk, в идеале хотелось бы так:

Код

function start(mes){
    alert(mes);
}

function process(mes){
    setTimeout('alert('+mes+')', 3000);
}

function end(){
    alert('the end!');
}

chain(start('Hello!'), process('processing'), end());


Но к сожалению уже на уровне передачи вызовов функций в качестве параметров для chain эти функции пытаются выполниться, так что можно что-то вроде :

chain([{ 'start' : ['Hello!']}, {'process' : ['processing']}, {'end' : []});
PM MAIL   Вверх
ksnk
Дата 28.1.2008, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



Ну, более реально записать вот так
Код

 chain(start,'Hello!')
.chain(end)

...
function chain(){
  var x ;
  if(!this instanceof chain) {
    x=new chain();
    x.chain=chain;
  } else 
    x=this;

  // тут разбираемся с количеством и типом передаваемых нам переменных
 ...

  return $x;
}

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


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Dr.Zlo
Дата 28.1.2008, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



В том-то и дело, у меня часто получаются конструкции из серии:

Код

function animate(){
    $('#x').animate({'color' : 'red'}, speed, 'linear', function(){
        $('#y').animate({'width': 300, 'height': 400}, speed*2, 'linear', function(){
            $('#z').css({'display': 'block','color' : 'red'}).animate({'marginLeft' : 100, 'marginTop': 100}, speed/2, 'linear', function(){
                return callback();
            })
        })
    })
}


Так что если никто не знает как на завершение функции повесить событие, придётся мне смириться с тем, что в каждой функции нужно будет прописывать callback, что-нибудь вроде chain.next();

Кстати, тут ещё одна проблема. Если есть функция например с тремя параметрами start(a,b,c) и мы пишем:

 chain(start : ['first', 'second', 'third']).chain(...).chain(...)

Как потом в теле chain вызвать start и передать ему эти 3 параметра? и что будет если один параметр- строка, второй- объект, 3й например число?
PM MAIL   Вверх
ksnk
Дата 28.1.2008, 18:17 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



Решением было бы зафиксировать интерфейс "функций анимации". К примеру - они принимают 2 параметра. Первый - параметри или  объект с реальными параметрами функции, второй - callback, который должен вызваться по завершении функции. если CallBack'а нет - не вызываем ничего.

тогда примерно вот что получается
Код

function chain(a,b){
  var x ;
  if(!(this instanceof Array)) {
    x=new Array();
//  } else if(!x.chain){
    // initialize new chain object
    x.chain=chain;
    // замкнем на себя указатель на себя
    x.next=(function(){
        var xx=x; 
        return function(){
            var x=xx.shift();
            if(x && x.a)
                x.a(x.b,xx.next)
        }
    })() ;
  } else {
    x=this;
  }
  x.push({a:a,b:b});
  return x;
}
// ***************************************
// функции анимации
function start(mes,callback){
    alert(mes);
    if(callback)callback();
}

function process(mes,callback){
    setTimeout(function(){alert(mes);if(callback)callback()}, 3000);
}

function end(x,callback){
    alert('the end!');
    if(callback)callback();
}

function compare(x,callback){
    if (x.first!=x.second)
        alert('Wow');
    else
        alert('Ok')
    if(callback)callback();
}
//**************************
// цепочка....
 chain(start,'Hello!')
.chain(process,'Message')
.chain(compare,{first:'Hello', second:'World'})
.chain(end)
.next();





--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 29.1.2008, 01:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



В соседней теме я уже даже сие применил... правда не особо показательно...
Окончательный интерфейсные соглашения
  • все функции анимации получают 2 параметра, второй из которых callback, обязательный к вызову по завершении операции.
  • Все функции анимации, ОБЯЗАНЫ использовать setInterval вместо setTimeout. Делить и разбираться сразу с двумя типами хандлеров мне показалось слишком круто.
  • Все функции анимации, использующие setInterval обязаны выдавать его как результат своей работы.
  • Никакой другой результат выдаваться не должен
Описание внешних функций chain 
chain() - вызов без параметров инициализирует новый объект chain 
chain(a,b) - поместить в очередь вызов функции a с праметрами b и chain.next
chain.run() - запуск очереди на выполнение. Можно вызывать неоднократно. Уже запущеннй процесс так не остановить.
chain.clear() - остановить цепочку

Каждая внешняя функция возвращает себя как результат и по этому можно писать цепочки таких вызовов...


Листинг функции chain
Код

// ***********************************
// chain
function chain(a,b,flag){
    var x ;
    if(!(this instanceof Array)) {
        x=new Array();
        // initialize new chain object
        // just to clear internal timeout
        x.stop=function(){
            if(this.timeout) 
                clearInterval(this.timeout);
            this.running=false;
            this.timeout=null;
        }
        // is animation running
        x.running=false;
        // the chain function again
        x.chain=chain;
        // function stop current and clear all  pending animations
        x.clear=function(){
            this.stop();
            while(this.length) this.pop();
            return this;
        }
        // internal function - execute next pending function
        x.exec=function(x){
            if((this.running=(x && x.a)))
                this.timeout=x.a(x.b,this.next);
        };
        // замкнем на себя указатель на себя
        x.run=function(){
            if(this.running) return this;
            else return this.next();
        };
        // common callback function
        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
                if(xx.length)
                    xx.exec(xx.shift());
                return xx;
            }
        })() ;
    } else {
        x=this;
    }
    if(a) x.push({a:a,b:b,flag:flag});
    return x;
}





Это сообщение отредактировал(а) ksnk - 29.1.2008, 09:49


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Dr.Zlo
Дата 29.1.2008, 10:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 49
Регистрация: 12.11.2007

Репутация: 1
Всего: 2



ksnk, спасибо!

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

Код


function start_func(mes1, mes2, mes3){
    alert(mes1+mes2+mes3)
    if (start_func.next) start_func.next();
}

function alert_func(mes){
    alert(mes)
    if (alert_func.next) alert_func.next();
}

// ***********************************
// chain
function chain(a,b,flag){
    var x ;
    if(!(this instanceof Array)) {
        x=new Array();
        // initialize new chain object
        // just to clear internal timeout
        x.stop=function(){
            if(this.timeout) 
                clearInterval(this.timeout);
            this.running=false;
            this.timeout=null;
        }
        // is animation running
        x.running=false;
        // the chain function again
        x.chain=chain;
        // function stop current and clear all  pending animations
        x.clear=function(){
            this.stop();
            while(this.length) this.pop();
            return this;
        }
        // internal function - execute next pending function
        x.exec=function(x){
            if((this.running=(x && x.a)))
                a.next = this.next;
                // Передаем 10 переменных про запас, это конечно идиотизм, но как сделать по-другому я не знаю
                this.timeout=x.a(x.b[0], x.b[1], x.b[2], x.b[3], x.b[4], x.b[5], x.b[6],x.b[7],x.b[8],x.b[9])
        };
        // замкнем на себя указатель на себя
        x.run=function(){
            if(this.running) return this;
            else return this.next();
        };
        // common callback function
        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
                xx.exec(xx.shift());
                return xx;
            }
        })() ;
    } else {
        x=this;
    }
    if(a) x.push({a:a,b:b,flag:flag});
    return x;
}


Вызов будет таким:

chain(start_func , ['Превед ',  ' всем ', ' патцанам!']).chain(alert_func , ['stop!!!']).run()

Возникает 2 проблемы:
1. После выполнения последнего элемента chain,  FireBug выдаёт ошибку:
"x has no properties"

2.  this.timeout=x.a(x.b[0], x.b[1], x.b[2], x.b[3], x.b[4], x.b[5], x.b[6],x.b[7],x.b[8],x.b[9]) - это позор программиста, но если никто не знает как это обойти и при этом передавать в функцию все переменные в нормальном виде, а не объектом, то я лучше оставлю так

Это сообщение отредактировал(а) Dr.Zlo - 29.1.2008, 14:03
PM MAIL   Вверх
ksnk
Дата 29.1.2008, 15:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



1. У меня чего-то не выдает... :(
2. написать вот так               

Код

              x.a.next = this.next; // <<< тут было неправильно!!!
                // Передаем 10 переменных про запас, это конечно идиотизм, но как сделать по-другому я не знаю
              this.timeout=x.a.apply(null,x.b);

Я, в принципе думал, что нужно бы проверять тип второго параметра  и если это массив - вызвать apply, но меня остановило то, что в жизни бывают и "просто массивы" smile

Добавлено @ 15:26
для пущего единообразия можно писать вот так.
Код

function start_func(mes1, mes2, mes3){
    alert(mes1+mes2+mes3)
    if (arguments.callee.next) arguments.callee.next();
}

function alert_func(mes){
    alert(mes)
    if (arguments.callee.next) arguments.callee.next();
}

 правда это немного подлинее  ;-)

Это сообщение отредактировал(а) ksnk - 29.1.2008, 15:26


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 29.1.2008, 15:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



1. У меня тоже выдает....  :-( Посмотрим - поковыряем...


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
ksnk
Дата 29.1.2008, 17:24 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: 84
Всего: 386



Вот вариант, который получился в итоге. Правила оформления функций анимации несколько поменялись... 
Код

/**
 * Инициализация  цепочки  исполняемых  функций.
 * 
 * !!!!!!!!!интерфейсные соглашения о "функциях анимации"!!!!!!
 * каждая  функция  анимации  обязана  по  своему  завершению обеспечить вызов
 *     такой  функции  arguments.callee.next  либо передав ее callback'ом кому
 *     нибудь                 либо                 явно                 вызвав
 *     if(arguments.callee.next)arguments.callee.next();
 * 
 * Все  функции  анимации, ОБЯЗАНЫ использовать setInterval вместо setTimeout.
 *     Делить  и  разбираться  сразу  с  двумя типами хандлеров мне показалось
 *     слишком круто.
 *     
 * Все  функции  анимации,  использующие  setInterval обязаны выдавать его как
 *     результат своей работы.
 *     
 * Если  функция использует setInterval и не выдает его в качестве результата,
 *     исполнение этой функции не будет остановлено вызовом chain.clear();
 *     
 * Никакой другой результат выдаваться не должен
 *        
 * !!!!!!!!Описание внешних функций Chain!!!!!!!!!
 * 
 * new Cain() - вызов без параметров инициализирует новый объект Chain
 * chain.chain(a,b)  -  поместить  в  очередь  вызов функции a с праметрами b.
 *     Если   функция   принимает   несколько   параметров  -  все  параметры,
 *     необходимые  для  нее  передаются  в  массиве b. если функция принимает
 *     единственный параметр - массив - он ОБЯЗАН быть передан как [b]
 * chain.run()  -  запуск  очереди на выполнение. Можно вызывать неоднократно.
 *     Если процесс уже идет вреда не будет.
 * chain.wait(ms) - вставить в цепочку команду задержки на ms миллисекунд    
 * chain.clear() - остановить выполнение цепочки
 * 
 * Каждая  внешняя  функция  возвращает  собственный объект как результат и по
 *     этому можно писать цепочки таких вызовов...
 *     
 * @param {Object} a
 * @param {Object} b
 * @param  {Object}  flag  - установка этого параметра помечает ОБЯЗАТЕЛЬНОСТЬ
 *     вызова этого метода даже в случае clear()
 * @example var x=new Chain(); x.wait(3000).chain(exec,'xxx').run();
 * @example Chain(show,'xxx').wait(3000).chain(exec,'xxx').run();
 * @example примеры функций анимации
 * 
 * function collapse(o,_to){
 *       return animate(o,_to,0,arguments.callee.next);
 * }
 * 
 * function hideit(o){
 *     o.style.display='none';
 *     o.style.width='0px';
 *     if(arguments.callee.next)arguments.callee.next();
 * }
 */
function Chain(a,b,flag){
    var x =new Array();//Thanks 2 AKS ;-)
    x.running = false;
    // совершенно приватная функция
    // остановим таймаут, если есть...
    function stop(){
        if(x.timeout) 
            clearInterval(x.timeout);
        x.running=false;
        x.timeout=null;
    }
    // выполним следующий элемент
    function exec(xx){
        if ((x.running = (xx && xx.a))) {
            xx.a.next=x.next
            x.timeout = xx.a.apply(null,xx.b);
        }    
    };
    // common callback handler
    // замкнем на себя указатель на себя
    x.next = function(){ 
        stop();
        if (x.length) {
            exec(x.shift());
        }
        return x;
    }
    
    // main chain function
    x.chain=function(a,b,c){
        if(!a) return this;
        if(!(b instanceof Array)) b=[b];
        this.push({a:a,b:b,flag:c});
        return this;
    }
    
    // perform waiting in a row
    x.wait=function(ms){
        var xx=this;
        return this.chain(function (ms){
                return setInterval(xx.next,ms)
            },ms)
    }
    
    // stop all pending animation
    x.clear=function(){
        stop();
        var x={};
        while(this.length && (!x.flag)) x=this.shift();
        if(x && x.flag) exec(x);
        return this;
    }
    
    // run the chain in case it stopped
    x.run=function(){
        if(this.running) return this;
        else return this.next();
    };
    
    return x.chain(a,b,flag);
}


P.S. поправлена ошибка в  функции chain.wait...
P.P.S почищен код, благодаря AKS
P.p.P.S поправлен метод clear, из-за чего были глюки с исполнением "аварийного" chain'а

Это сообщение отредактировал(а) ksnk - 30.1.2008, 17:09


--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
Страницы: (3) Все [1] 2 3 
Ответ в темуСоздание новой темы Создание опроса
Форум для вопросов, которые имеются в справочниках, но их поиск вызвал затруднения, или для разработчика требуется совет или просьба отыскать ошибку. Напоминаем: 1) чётко формулируйте вопрос, 2) приведите пример того, что уже сделано, 3) укажите явно, нужен работающий пример или подсказка о том, где найти информацию.
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0699 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.