Асинхронные вызовы с асинхронным ответом в NodeJS - PullRequest
4 голосов
/ 19 августа 2011

Я строю свой первый проект с NodeJS в эти дни, но я немного запутался в задаче. Я считаю, что это простая задача. Думаю, проблема в том, что я недостаточно осведомлен об этом асинхронном подходе, но не могуотвечай куда угодно.

У меня есть простой цикл, повторяющийся над массивом, и для любого элемента, основанного на каком-то правиле, я буду вызывать ту или иную функцию.Теперь некоторые операции будут выполняться быстрее, чем другие, поэтому я мог бы получить функцию для элемента N, возвращающуюся раньше, чем функция для элемента N-1.Чтобы упростить это как-то так

for (var i = 0 ; i < 10 ; i++) {
      if (i%2 === 0) {
          setTimeout(function(i) {
               console.log(i);
          }, 2000);
      }
      else { console.log(i); }
}

, чтобы любое четное число печаталось с задержкой в ​​2 секунды, а нечетные числа - сразу.В любом случае, запустив его, я получаю

1
3
5
7
9

<<2 seconds break>>

undefined
undefined
undefined
undefined
undefined

, похоже, четное значение «потеряно».Как я могу передать значение, убедившись, что функция не потеряет входное значение?Я что-то пропустил?

Спасибо, Мауро

Ответы [ 3 ]

12 голосов
/ 19 августа 2011

Ваша аргументная функция setTimeout объявлена ​​для принятия одного параметра, i.Когда setTimeout вызывает функцию без аргументов, как это происходит, параметр, таким образом, устанавливается на undefined.

Казалось бы, это будет немного лучше, поскольку он больше не затеняет внешнее iпеременная, на которую вы первоначально пытались сослаться ...

setTimeout(function() {
  console.log(i);
}, 2000)

... но если вы запустите ее, вы обнаружите, что она печатает 10 5 раз, потому что каждая созданная вами функция ссылается на одну и ту же i переменная, значение которой будет 10, когда условие выхода из цикла становится true и оно завершается.

Создание замыкания , которое содержит значение i в том виде, как оно былово время цикла, в котором была создана каждая setTimout функция аргумента, все получится:

setTimeout((function(i) {
  return function() {
    console.log(i);
  }
})(i), 2000)

Переименование аргумента в выражение немедленного вызова функции , которое мы только что использовали, может помочь сделать вещиclearer:

setTimeout((function(loopIndex) {
  return function() {
    console.log(loopIndex);
  }
})(i), 2000)

Мы:

  • Создаем функцию, которая принимает один аргумент и возвращает другую функцию.
  • Немедленно вызывая «внешнюю» функцию, паспойте ему текущее значение i (которое не является объектом, поэтому эффективно передается по значению).
  • Функция "external" возвращает функцию "inner", которая передается в качестве аргумента setTimeout.

Это работает, потому что:

  1. При создании функции в JavaScript создается новая область для хранения переменных аргумента функции илюбые другие переменные, объявленные в нем с использованием ключевого слова var.Думайте об этом как о невидимом объекте со свойствами, соответствующими именам переменных.

  2. Все функции содержат ссылку на область, в которой они были определены, как часть их цепочки областей действия .Они по-прежнему имеют доступ к этой области, даже если она больше не «активна» (например, когда возвращается функция, для которой она была создана).«Внутренняя» функция была создана в области видимости, которая содержала переменную loopIndex, установленную на значение i во время вызова IIFE.Поэтому, когда вы пытаетесь сослаться на переменную с именем loopIndex изнутри внутренней функции, она сначала проверяет свою собственную область (и не находит там loopIndex) , а затем начинает подниматься по цепочке областей действия -во-первых, он проверяет область, в которой он был определен, в котором содержит переменную loopIndex, значение которой передается в console.log().

Вот и все замыкание - функция, которая имеет доступ к области, в которой она была определена, даже если функция, для которой была создана область, завершила выполнение.

1 голос
/ 19 августа 2011

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

for (var i = 0 ; i < 10 ; i++) {
  if (i % 2 === 0) {
    setTimeout(function(num) {
      console.log(num);
    }.bind(this, i), 2000);
  } else {
    console.log(i);
  }
}

В этом случае к функции присоединено .bind(this, i).this только для заполнения;первый аргумент bind - это всегда то, что вы хотите this разрешить, когда внутри связанной функции.Однако после этого мы можем передать любые значения, которые мы хотим добавить к аргументам связанной функции - в этом случае мы хотим, чтобы текущее значение i (когда функция была привязана) было переданофункция.

Подробнее о bind можно прочитать в Документах MDN .

.
0 голосов
/ 19 августа 2011
    setTimeout(function(i) {
           console.log(i);
      }, 2000);

Функции, вызываемые из setTimeout, не будут вызываться ни с одним параметром.Вам нужно создать замыкание.

Извлечь этот вопрос и принятый ответ.

(Это не проблема для node.js)

...