Javascript анонимные функции и глобальные переменные - PullRequest
4 голосов
/ 18 мая 2010

Я думал, что постараюсь быть умным и создать собственную функцию ожидания (я понимаю, что есть и другие способы сделать это). Итак, я написал:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval) {
  countdowntimer = wait_interval;

  interval_id = setInterval(function() {
    --countdowntimer <=0 ? clearInterval(interval_id) : null;
  }, 1000);

  do {} while (countdowntimer >= 0);
}

// Wait a bit: 5 secs
Wait(5);

Это все работает, кроме бесконечного цикла. При проверке, если я возьму из цикла «Пока», анонимная функция вводится 5 раз, как и ожидалось. Очевидно, что глобальная переменная countdowntimer уменьшается.

Однако, если я проверяю значение countdowntimer , в цикле «Время» оно никогда не падает. И это несмотря на то, что анонимная функция вызывается в то время как в цикле while!

Ясно, что почему-то есть два значения countdowntimer , но почему?

EDIT

Хорошо, теперь я понимаю, что Javascript является однопоточным. И это - своего рода - отвечает на мой вопрос. Но в какой момент обработки этого единственного потока происходит так называемый асинхронный вызов с использованием setInterval ? Это только между вызовами функций? Конечно, нет, как насчет функций, выполнение которых занимает много времени?

Ответы [ 4 ]

5 голосов
/ 18 мая 2010

Не осталось двух копий переменной. Javascript в веб-браузерах - однопоточный (если вы не используете новый материал для веб-рабочих ). Таким образом, анонимная функция никогда не может работать, потому что Wait связывает интерпретатор.

Вы не можете использовать функции занятости в браузерном Javascript; больше ничего не случится (и это плохая идея в большинстве других сред, даже там, где это возможно). Вы должны использовать обратные вызовы вместо этого. Вот минималистская переделка этого:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval, callback) {
    countdowntimer = wait_interval;

    interval_id = setInterval(function() {
        if (--countdowntimer <=0) {
            clearInterval(interval_id);
            interval_id = 0;
            callback();
        }
    }, 1000);
}

// Wait a bit: 5 secs
Wait(5, function() {
    alert("Done waiting");
});

// Any code here happens immediately, it doesn't wait for the callback

Редактировать Ответ на ваше сообщение:

Но в какой момент обработки этого единственного потока происходит так называемый асинхронный вызов с использованием setInterval? Это только между вызовами функций? Конечно, нет, как насчет функций, выполнение которых занимает много времени?

В значительной степени, да & mdash; и поэтому важно, чтобы функции не были долговременными. (Технически это даже не между вызовами функций, в том случае, если у вас есть функция, которая вызывает три другие функции, интерпретатор не может делать что-либо еще, пока эта (внешняя) функция работает.) Интерпретатор по существу поддерживает очередь функций, в которых он нуждается выполнить. Он начинается с выполнения любого глобального кода (скорее как большой вызов функции). Затем, когда что-то происходит (события пользовательского ввода, достигается время для вызова обратного вызова, запланированного с помощью setTimeout и т. Д.), Интерпретатор помещает вызовы, необходимые для выполнения, в очередь. Он всегда обрабатывает вызов в начале очереди, и поэтому все может сложиться (например, ваши setInterval вызовы, хотя setInterval - это bit special & mdash; он не будет ставить в очередь последующий обратный вызов если предыдущий все еще находится в очереди, ожидая обработки). Поэтому подумайте о том, когда ваш код получает контроль и когда он освобождает контроль (например, путем возврата). Переводчик может только делать другие вещи после того, как вы отпустите контроль и перед тем, как он вернет его вам снова. И опять же, в некоторых браузерах (например, IE) этот же поток также используется для рисования пользовательского интерфейса и т. Д., Поэтому вставки DOM (например) не будут отображаться, пока вы не вернете элемент управления обратно в браузер, чтобы он мог получить на том, чтобы делать свою живопись.

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

// Non-functional non-event-driven pseudo-example
askTheQuestion();
answer = readTheAnswer();      // Script pauses here
doSomethingWithAnswer(answer); // This doesn't happen until we have an answer
doSomethingElse();

Это не работает в мире событий. Вместо этого вы делаете это:

askTheQuestion();
setCallbackForQuestionAnsweredEvent(doSomethingWithAnswer);
// If we had code here, it would happen *immediately*,
// it wouldn't wait for the answer

Так, например, askTheQuestion может перекрывать элемент div на странице с полями, запрашивающими у пользователя различные фрагменты информации, с кнопкой «ОК», по которой они могут щелкнуть, когда все будет готово. setCallbackForQuestionAnswered действительно будет перехватывать событие click на кнопке «ОК». doSomethingWithAnswer будет собирать информацию из полей, удалять или скрывать div и что-то делать с информацией.

3 голосов
/ 18 мая 2010

Большинство реализаций Javascript однопоточные , поэтому, когда он выполняет цикл while, он не позволяет выполнять что-либо еще, поэтому interval никогда не запускается, пока работает while , таким образом, делая бесконечный цикл.

Существует много подобных попыток создать функцию sleep / wait / pause в javascript, но, поскольку большинство реализаций являются однопоточными, она просто не позволяет вам делать что-либо еще во время сна (!).

Альтернативный способ сделать задержку - написать timeouts . Они могут отложить выполнение фрагмента кода, но вы должны прервать его во многих функциях. Вы всегда можете встроить функции, чтобы им было легче следить (и совместно использовать переменные в одном контексте выполнения).

Есть также некоторые библиотеки, которые добавляют синтетический сахар в javascript, делая его более читабельным.

EDIT: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} * * * * * * * * *} * * * * * * * * * * * * * * * * * * *}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} [[[[•] Он в значительной степени объясняет это в деталях. Надеюсь, это поможет.

2 голосов
/ 18 мая 2010

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

Есть причина, по которой никто не делал Wait раньше (а многие пытались); это просто невозможно сделать.

Вам придется прибегнуть к разбивке вашей функции на биты и составить расписание с помощью setTimeout или setInterval.

//first part
...
setTimeout(function(){
    //next part
}, 5000/*ms*/);

В зависимости от ваших потребностей это может (должно) быть реализовано как конечный автомат.

0 голосов
/ 18 мая 2010

Вместо того, чтобы использовать глобальную переменную countdowntimer, почему бы просто не изменить атрибут миллисекунды на setInterval? Что-то вроде:

var waitId;

function Wait(waitSeconds)
{
    waitId= setInterval(function(){clearInterval(waitId);}, waitSeconds * 1000);
}
...