Модераторы: 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   Вверх
Dr.Zlo
Дата 29.1.2008, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Я избавился от ошибки следующим образом:

Код

        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
        if (xx.length==0) return false; // Если цепочка закончилась, уходим
                xx.exec(xx.shift());
                return xx;
            }
        })() ;


После этого нормально стало всё работать и не косячить после последнего элемента. Если честно я до сих пор до конца не втыкаю как работает твой chain, это решение я нашёл интуитивно.
Но по ходу ты пофиксил тем же способом и ужё ушёл намного дальше в разработку chain smile
Вечером посмотрю и протестю твой новый вариант.
PM MAIL   Вверх
AKS
Дата 29.1.2008, 20:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


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

Репутация: 27
Всего: 52



Цитата(ksnk @  29.1.2008,  17:24 Найти цитируемый пост)
var x =(this instanceof Array)?this:new Array();

Смотрю и не могу понять - когда условие this instanceof Array будет истинным? Когда Вы "провернете" трюк из книжки?

PM MAIL   Вверх
ksnk
Дата 29.1.2008, 21:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



AKS, Нееееее! Тут как раз все значительно интереснее  ;-) объект chain это и ЕСТЬ настоящий массив честно сделанный через new Array!

Добавлено через 42 секунды
this instanceof Array - проверка на то, вызывают ли нас первый раз. Тогда this указывает на объект window


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


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


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

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



AKS, Впрочем, Вы как всегда правы! smile Проверка идиотская и в этом варианте лишняя. В оправдание могу предъявить цепочку соображений, которая привела к появлению этого кода.

Первоначальное решение было опубликовано выше несколькими постами и последующие варианты лишь "облагораживали" его.

Проявившееся желание написать chain(x).chain(y).chain(z)... породило совершенно логичную мысль о том, что каждая функция chain выдает некий объект как результат. причем первый chain в цепочке должен его (этот объект) породить. А так как по сути первый и последующие элементы цепочки одинаковы по входным параметрам, то родилась уникальная мысль написать конструкцию 
function chain(){..var x = new chain().... this.chain=chain/* Yoooo!!!*/ ...}; chain.prototype=new Array();,
 все в одном флаконе. ;-)
Когда пришла пора "со всей этой фигней взлетать", возникла задача по определению контекста вызова chain(), new chain(), и .chain'а, рудиментарное наследие от которой до сих пор сидела в коде. по дороге напоролся на "баг", не позволяющий наследоваться от Array запланированным способом. Хорошо, что внезапно посетивший меня приступ здравого смысла заставил несколько оптимизировать код, но, к сожалению, не до конца.
Так что, AKS, спасибо!


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


Новичок



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

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



ksnk, вчера внедрил chain в довольно большое приложение. Всё отлично работает, 

Но есть несколько пожеланий. 

1. Иногда нужно собрать chain по кусочкам, и только потом выполнить, или к примеру иногда удобней объявить chain где-нибудь в начале скрипта, а вызывать в нужных местах. Допустим у нас есть задача: для всех чётных элементов списка выполнить функцию по цепочке. Вот как я это вижу: 
 
Код

var objs=document.getElementsByTagName('li');
var x=new Chain();
for(var i=0; i<objs.length; i++){
    if ((Math.floor((i+1)/2)-Math.floor((i)/2))==0){
        var obj = objs[i]
        x.chain(animate, obj);
    }
}
x.run();


Было бы прикольно, если бы такая конструкция работала

2. После завершения chain лучше избавляться от созданного массива (обnullять его, что-ли), ведь если все эти массивы будут висеть в памяти, после нескольких вызовов chain скрипт будет заметно тормозиться
PM MAIL   Вверх
ksnk
Дата 30.1.2008, 09:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Dr.Zlo @  30.1.2008,  09:32 Найти цитируемый пост)
 Иногда нужно собрать chain по кусочкам, и только потом выполнить,

До тех пор пока не вызван метод run, chain остается "не запущеным".
Цитата(Dr.Zlo @  30.1.2008,  09:32 Найти цитируемый пост)
После завершения chain лучше избавляться от созданного массива
 Факт вызова очередной функции chain'а "автоматически" выкидывает первый элемент... как рожок в автомате - патрон стреляет - гильза вылетает smile

imho, если переписать вашу конструкцию вот так
Код

var objs=document.getElementsByTagName('li');
var x=Chain(); // идеология поменялась... new вызывать не надо...
for(var i=0; i<objs.length; i++){
    if (!( i | 1)) { // четные
        x.chain(animate, objs[i]);
    }
}
x.run();

То я не вижу причин почему бы она не заработала...

т.е. Все уже сделано ;-)

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


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


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


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

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



Честно говоря меня преследует вопрос эстетический ;-) Удобно ли то, что "первая" функция называется Chain (с большой буквы). Можно было бы ради единообразия назвать ее chain (с маленькой), как и "собственный" метод. На каком-то этапе мне показалось идеологически более верно то, что первый элемент цепочки пишется с большой буквы. Правда до серьезного практического применения этой "идеологии" я пока не добрался. 


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


Новичок



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

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



По поводу именования. 
Думаю по фэн-шую было бы правильно создавать цепь, потом присоединять к ней звенья. Цепь по-английски chain, по поводу звеньев яндекс словари говорят следующее:

звено
ср.

1) link

2) (механизма или структуры) unit, section; part, branch

3) team, group, unit

4) (самолетов) flight

5) воен. level, echelon, squad 

Так как с link будут несколько другие ассоциации, то можно взять unit для определения звена цепи. 
В итоге можно договориться использовать конструкцию вроде:

chain().unit(func1, param1).unit(func2, param2).unit(func3, param3).run();

В крайнем случае оставить всё как есть, только избавиться от Chain с большой буквы, он путает и сбивает с толку, как мне кажется
PM MAIL   Вверх
ksnk
Дата 30.1.2008, 11:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



ok! Надо будет при случае поменять большую букву...
Насчет unit'а - я против... ;-) ; - я предполагал первонально, что будет вот так
chain(func1, param1).chain(func2, param2).chain(func3, param3).run(); Единая сущьность для первой и последующих итераций... Imho, пусть так и остается. Всех изменений в скрипте, если надо - переназвать "головную" функцию...

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


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


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Уже вторая страница, а я что-то не пойму сути вопроса... smile
Оно?
Код

        function chain(func) {
            var prev = this; //ссылка на предыдущую chain
            var args = []; //из-за особого чувства юмора у ECMA, arguments это не Array, метода .slice нет...
            for(var i = 1; i < arguments.length; i++) args.push(arguments[i]);

            var hnd = function() {
                if(prev instanceof Function) prev(); //у самого первого chain здесь будет window или любой другой объект, чьим методом была chain
                func.apply(window, args);
            }

            hnd.chain = arguments.callee; //цепочка
            return hnd;
        }

        chain(alert, "OK").chain(alert, "cool")(); //результат chain - функция, не забываем её вызвать



--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Dr.Zlo
Дата 30.1.2008, 14:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Всё-таки отсроченный запуск chain не работает, я тестировал, просто не запускается и всё. Вот исходник:

Код

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<script language="javascript" src="jquery.js"></script>
<script>
$(document).ready(function(){
    var objs=document.getElementsByTagName('li');
    var x=Chain(); 
    for(var i=0; i<objs.length; i++){
        if ( i & 1) { // твой вариант определения чётности был не правильный
            //cur_li(objs[i])
            x.chain(cur_li, objs[i]);
        }
    }
    x.run();
})

function cur_li(obj){
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (cur_li.next) cur_li.next();
    })
}

</script>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
</ul>
</body>
</html>


Кстати, ещё одна интересная тема для размышлений- можно ли chain замкнуть на саму себя, чтоб все операции выполнялись в цикле заданное количество раз, либо бесконечно, пока мы насильно не остановим chain?

Добавлено @ 15:08
Sardar, в твоём случае функции выполняются одновременно, а суть chain состоит в том, чтоб выполнять их последовательно, чтоб к примеру сначала выполнилась анимация, после завершения анимации выполнилась логика, после завершения логики вторая анимация и т.п. В этом-то и суть вопроса smile

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


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


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

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



Цитата

 // твой вариант определения чётности был не правильный

Согласен... Зато твой - определение "нечетности", а не "четности" ;-)

Вот такая правка исправит ситуацию... Первым элементом цепочки оказывалась undefined конструкция, которая генерировалась Chain()'ом...
Код

    // 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;
    }






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


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


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

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



Да , еще вот это я поменял
Код

function cur_li(obj){
    var xx= cur_li.next ; 
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (xx) xx();
    })
}


Добавлено через 57 секунд
Хотя, как было - тоже работает ...



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


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


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

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



Обновил листинг функции... теперь в этом примере она еще и прекращать анимацию, в том числе и от jQuery,  умеет корректно...
Код

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<script language="javascript" src="jquery.js"></script>
<script>
var CHAIN=Chain(); // <<< Это, чтобы на кнопочку повесить ! :-)

function cur_li(obj){
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (cur_li.next) cur_li.next();
    })
}
function stop_animation(obj_array){
    var i=obj_array.length;
    while(i--){
         $(obj_array.shift()).stop(); // clear all animation напроч!!!
    }
   stop_animation.next();
}

function run_animation(){
    var objs=document.getElementsByTagName('li');
    var x=CHAIN,par=[]; // <<< objs - это не массив...
    for(var i=0; i<objs.length; i++){
        if ( i & 1) { 
            //cur_li(objs[i])
            par.push(objs[i])
            x.chain(cur_li, objs[i]);
        }
    }
    x.chain(stop_animation,[par],'require') // // <<< последний параметр - не важно что, лишь бы был...
    .run();
}

$(run_animation);

</script>
</head>
<body>
<div id='debug' style='float:right;'></div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
</ul>
<input type='button' onclick='run_animation()' value='Run animtion again'>
<input type='button' onclick='CHAIN.clear()' value='Stop animation'>
</body>
</html>



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


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(Dr.Zlo @  30.1.2008,  13:58 Найти цитируемый пост)
Sardar, в твоём случае функции выполняются одновременно, а суть chain состоит в том, чтоб выполнять их последовательно,

А ты попробуй smile  Функции вызываются в порядке следования, одна закончиться сразу следующая, без паузы.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
ksnk
Дата 30.1.2008, 19:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Sardar, имеется ввиду, что в цепочке могут присутствовать функции, которые не заканчиваются фактом своего вызова. К примеру, выставляющие мрачный таймаут и чекающие какое-нибудь странное состояние. Про такие функции только она сама может сказать, когда-же она пожелает закончится ;-) Пример - получение Ajax'ом странички. Я сейчас активно думаю над  возможностью писать 
chain(get_ajax,'mypage.php?action=hello')
.chain(pace_into_div,[null,null,'div_id']) // null - placeholder для RequestText, RequestError

Моя схема позволяет вызываемой функции самой определить время своего завершения и вызвать следующий элемент в цепочке... А Ваша? smile

Добавлено через 9 минут и 1 секунду
Цитата(Dr.Zlo @  30.1.2008,  14:58 Найти цитируемый пост)
Кстати, ещё одна интересная тема для размышлений- можно ли chain замкнуть на саму себя, чтоб все операции выполнялись в цикле заданное количество раз, либо бесконечно, пока мы насильно не остановим chain?

Хм... вставлять в chain операторы ветвления по условию, циклы и свитчи... + парраллельное исполнение анимаци... smile Соблазнительно, но пока думаю, что это ненужный геморой...
В принципе, можно как-то обойтись и существующей схемой. Достаточно иметь указатель на головной chain в исполняемой процедуре. Сама процедура уже может навставлять в массив всего чего пожелает... Надо только разумно ограничить ее желания ;-)
В принципе, указатель на головной chain можно получить, вызвав arguments.calee.next с каким- нибудь характерным параметром... В общем - нужно подумать...

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


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


Новичок



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

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



Кстати, вот примитивная реализация цикличности. Тупо и забавно smile

Код

$(document).ready(function(){
    loop_chain();
})

function loop_chain(){
  var obj =$('#animate_div');
  var speed = 500;
  Chain(animate, [obj, speed]).chain(animate2, [obj, speed]).chain(loop_chain).run();
}


function animate(obj, speed){
    $(obj).animate({'width': '1000', 'height': '1000'}, speed, 'linear', function(){
        if (animate.next) animate.next();
    })
}

function animate2(obj, speed){
    $(obj).animate({'width': '100', 'height': '100'}, speed, 'linear', function(){
        if (animate2.next) animate2.next();
    })
}


Добавлено через 3 минуты и 45 секунд
Кстати, 
Цитата

Согласен... Зато твой - определение "нечетности", а не "четности" ;-)

не забываем, что в массивах чётные элементы имеют нечётные индексы ;-)
PM MAIL   Вверх
ksnk
Дата 9.2.2008, 01:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



В функции обнаружился баг. Связан он с хранением "цепочки" в самой функции анимации. Если используется анимирование сразу в несколько потоков, цепочки загадочным образом путаются с труднопредсказуемым результатом... Было принято решение откатится до фиксированного формата функций анимации. Правда здравый смысл все-таки немного победил, теперь они получают 3 параметра, 2 из которых - нормальные параметры, а 3-й callback функция для вызова. Во вложении сам chain и примерчик его использования.

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

Присоединённый файл ( Кол-во скачиваний: 2 )
Присоединённый файл  chain.zip 2,84 Kb


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


 




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


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

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