Javascript заставляет функции выполняться одна за другой

У меня есть некоторые функции, все настроено. Один эффект набора текста, один для абзацев, другой для добавления " " и один для мигающего курсора, как в терминале. Все это изменяет innerHTML div.

Когда страница загружается, функция Start() имеет внутри около 10 функций, которые составляют последовательность того, что должно произойти: текст написан, курсор начинает мигать, абзац, текст снова пишется и т.д. и т.п.

Дело в том, что все они выполняются одновременно, если я не использую setTimeout() для каждого из них внутри функции Start(). И это немного запутанно, учитывая, что я должен определить время начала для каждой из функций.

РЕДАКТИРОВАТЬ: здесь нет JQuery. Просто джаваскрипт. Это весь мой файл JS:

ctrl = 0;
y=0;
block = 0;
test = "";
first_time = 1;

function typing(id, sentence){

        var index=0;
        var intObject= setInterval(function() {
               document.getElementById(id).innerHTML+=sentence[index]; 
               index++;
               if(index==sentence.length){
                     clearInterval(intObject);
                }
            }, 100);
}


function paragraph(x){
    while(x>0){
        document.getElementById("container").innerHTML+="<br>";
        x--;
    }
}

function advance(x){
    while(x>0){
        document.getElementById("container").innerHTML+="&nbsp;";
        x--;
    }
}


function blink(y){
    if(first_time == 1){ctrl = y; first_time=0;}
    if(ctrl!=0){
        if(block=='0'){
        test = document.getElementById("container").innerHTML;
            document.getElementById("container").innerHTML+="\u258B";
            block=1;
        }
        else if(block=='1'){
            document.getElementById("container").innerHTML=test;
            block=0;
        }
        ctrl--;
        setTimeout("blink(y);", 300);
    }
    if(ctrl==0){first_time=1;}
}


function start(){
    typing('container','Subject Name:');
    setTimeout("blink('4');",1700);
    setTimeout("typing('container',' Carlos Miguel Fernando');",2800);
    setTimeout("blink('6');",5600);
    setTimeout("paragraph('1');",7200);
    setTimeout("blink('8');",7400);
    setTimeout("typing('container','Age: 21');",9500);
    setTimeout("blink('4');",10800);
    setTimeout("paragraph('1');",12800);
    setTimeout("blink('4');",13200);
    setTimeout("typing('container','Location: Povoa de Varzim, Portugal');",14500);
    setTimeout("blink('14');",19000);
    setTimeout(function(){document.getElementById("more").style.display="block";}, 23000);
    setTimeout("typing('more','Find Out More');",24000);
}

person Carlos Miguel Fernando    schedule 05.12.2013    source источник
comment
Помимо того, что ужасно передавать строки в тайм-аут, решение сильно зависит от ваших функций. Они кажутся асинхронными, поэтому приходится работать с обратными вызовами или промисами. Возможно, очередь анимации — это то, что вам нужно. Если вы не возражаете, вы можете взглянуть на jQuery, где у вас уже реализована вся эта механика.   -  person Christoph    schedule 05.12.2013
comment
Проблема сложнее, чем кажется. Я предлагаю вам узнать об обратных вызовах и асинхронном программировании в JavaScript. Ищите эти термины: функции высшего порядка, промисы, продолжения. Это должно заставить вас начать.   -  person elclanrs    schedule 05.12.2013
comment
Просто отредактировал его и добавил весь мой файл javascript. Может быть, это несколько полезно. Я получу некоторую информацию об асинхронных функциях и обратных вызовах, ребята, спасибо!   -  person Carlos Miguel Fernando    schedule 05.12.2013
comment
stackoverflow.com/questions/899102/ и stackoverflow.com/questions/17528749/   -  person Satpal    schedule 05.12.2013


Ответы (2)


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

blink(4);
sleep(1000);   // wait 1 second
blink(4);

Реализуйте это так:

var sleep = function(ms) {
    var result = $.Deferred();
    setTimeout(result.resolve, ms);
    return result.promise();
};

т. е. создать $.Deferred и вернуть его promise(), но между ними начать какое-то действие, которое завершится позже. Когда это произойдет, вызовите resolve() (здесь я просто получаю setTimeout для прямого вызова). Вы можете передать значение resolve как логическое «возвращаемое значение» вашей функции. Также вы можете вместо этого вызвать reject, что логически похоже на исключение.

Когда у вас есть набор функций из стандартных блоков, которые возвращают промисы, вы можете сделать следующее:

typing('container','Subject Name:').then(function() {
   return blink('4');
}).then(function() {
   return typing('container',' Test');
}).then(function() {
   return blink('4');
}).then(function() {
   // and so on
});

ОБНОВЛЕНИЕ:

Нажмите здесь, чтобы просмотреть макет в jsbin.

person Daniel Earwicker    schedule 05.12.2013
comment
+1, потому что .. обещаниям нужно больше внимания. (Тем не менее, следует отметить, что jQuery 1.8+, вероятно, следует использовать для надлежащей поддержки then .. или, возможно, это была еще одна проблема с невыполненным обещанием. Существует также Q и так далее.) - person user2864740; 05.12.2013
comment
NB. Я считаю, что Q и jQuery различаются тем, что должно произойти, если resolve вызывается во второй раз для одного и того же обещания (Q бросает, jQuery игнорирует). - person Daniel Earwicker; 05.12.2013
comment
Получение Uncaught TypeError: Не удается вызвать метод «затем» неопределенной ошибки с этим, но он решает мою проблему, как только я заставляю его работать. Спасибо! - person Carlos Miguel Fernando; 05.12.2013
comment
@DanielEarwicker Интересное знание - я сам никогда не сталкивался с этим (пока). - person user2864740; 05.12.2013
comment
@ user2864740 - наверное хороший знак! Обычно это указывает на ошибку в вашей логике. Так что было бы лучше, если бы jQuery кидал, чтобы помочь нам найти такие ошибки. - person Daniel Earwicker; 05.12.2013
comment
@CarlosMiguelFernando - обязательно ознакомьтесь с jsbin, на который я ссылался, так как это рабочий пример. - person Daniel Earwicker; 05.12.2013
comment
@DanielEarwicker, я сделал и добавил это. Теперь у меня есть Uncaught ReferenceError: $ не определено, хотя я добавил ‹script src=ajax.googleapis.com/ajax/libs/jquery/2.0.2/› в мой HTML - person Carlos Miguel Fernando; 05.12.2013
comment
@CarlosMiguelFernando - похоже, вам нужно задать новые вопросы (или найти существующие) по SO, что-то вроде Как мне ссылаться на jQuery из моего приложения HTML/JavaScript? - person Daniel Earwicker; 05.12.2013
comment
@DanielEarwicker Большое спасибо за вашу помощь! - person Carlos Miguel Fernando; 05.12.2013

проверьте ниже код

function blink(id)
{
    //Do something
    alert(id);

    if(id == 2)
    {
        //call typing with text
        blink(4);
    }
    if(id == 4)
    {
        //call typing with text
        blink(6);
    }
    if(id == 6)
    {
        //call typing with text
        blink(8);
    }
    if(id == 8)
    {
        //Complete
    }
}

И вызовите blink(2); он будет вызывать функцию мигания одну за другой.

person Bhavesh Parekh    schedule 05.12.2013
comment
Проблема в том, что ввод вызова с текстом является асинхронным — он возвращается немедленно, но активность продолжается. Если blink сразу вызовет сам себя, то все шаги будут выполняться без задержки. Это ничем не будет отличаться от исходного нерабочего кода OP, где они просто вызывали функции последовательно. - person Daniel Earwicker; 05.12.2013