2013-08-09 4 views
1

Я (относительный) узел новичка, попадающий в систему, и весь энтузиазм в сообществе для «просто писать обратные вызовы, все асинхронно и управляться событиями, не волнуйтесь!» оставил меня немного запутанным относительно потока управления в рамках одной программы (или в более узких терминах, поток управления при обработке одного запроса в более крупной программе)Все функции «обратного вызова» узла «Потенциально асинхронные»?

Если у меня есть следующая программа, запускаемая под узлом

var foo = function(){ 
    console.log("Called Foo"); 
}; 

var bar = function(){ 
    console.log("Called Bar"); 
}; 

var doTheThing = function(arg1, callback){ 
    callback(); 
}; 

doTheThing(true, function() { 
    foo(); 
}); 
bar(); 

есть любой шанс, что foo выполнит послеbar? Когда я запускаю программу локально с помощью командной строки, это всегда

Called Foo 
Called Bar 

, но я вижу так много предупреждений из хорошо предназначенных евангелистов вдоль линий не думайте, что ваш обратный вызов будет вызываться, когда вы думаете, что будет , что я не знаю, будут ли они просто предупреждать меня о деталях реализации библиотеки, или если node.js делает что-то странное/специальное, когда вы используете объект функции в качестве параметра. не

+6

Нет никаких шансов. Все, что у вас есть, синхронно. Проблема возникает, когда вы используете асинхронные обратные вызовы (например, условия гонки setTimeout), или когда используемая библиотека использует асинхронные вещи (например, объекты jQuery '$ .Deferred'). ** РЕДАКТИРОВАТЬ: ** Я понял, что вы в основном говорите об узле, но я не понимаю, почему будет какая-то разница ... Надеюсь, я не ошибаюсь! Но не должно быть причин, по которым порядок будет отличаться, если вы не используете асинхронные функции ** конкретно ** (который не является вашим кодом) – Ian

+0

@ Я уверен, что вы правы, но я делал это достаточно долго, чтобы никогда не принимать что-либо о новой технологии, если я не проверил h-l и не получил подтверждения от сверстников. –

+0

Эй, вот почему я прокомментировал, надеясь, что больше людей присоединятся и обсудят :) – Ian

ответ

5

Нет, нет никаких шансов. Не для этого кода.

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

Это однако плохая практика делать предположения, как по двум причинам, во-первых, что только потому, что теперь это синхронное не означает кого-то другого, или забывания будущее, вы не можете изменить его позже, а во-вторых, потому, что если все это синхронно, почему вы/они используете обратные вызовы в первую очередь? Вся точка обратных вызовов - это возможность возможности асинхронных вызовов. Используя обратные вызовы, а затем действуя так, как будто они всегда будут синхронными, даже если вы знаете, что это так, делает ваш код запутанным для кого-то другого.

+1

Re: * если все синхронно, почему вы/они используете обратные вызовы в первую очередь * - передача анонимных функций вокруг имеет длинную и освященную историю в некоторых уголках мира программирования, независимо от того, выполняется ли код синхронно или асинхронно. –

+0

Правда. Такие вещи, как функции сортировки или функции сортировки php, выполняют функции и являются синхронными, и их нужно трактовать совершенно по-другому. Думаю, я мысленно не называю их «обратными вызовами», потому что проблемы у них разные. Вы по-прежнему должны быть очень осторожны с областью действия и не делать предположений о том, когда ваш callback будет вызван. –

2

Нет

Ваш пример кода 100% синхронные, однопоточный, просто сверху-вниз. Но это потому, что вы не делаете никаких операций ввода-вывода, не имеете настоящих асинхронных вызовов и не используете process.nextTick, setTimeout или setInterval. Для того, чтобы более реалистично имитировать асинхронные вызовы сделать что-то вроде:

function fakeAsync(name, callback) { 
    setTimeout(function() { 
    callback(null, name); 
    }, Math.random() * 5000); 
} 

function logIt(error, result) { 
    console.log(result); 
} 

fakeAsync('one', logIt); 
fakeAsync('two', logIt); 
fakeAsync('three', logIt); 

Run, что несколько раз, и вы увидите испорченные результаты иногда.

+0

«иногда»? Примерно 5/6 времени :-) –

2

Есть ли вероятность того, что foo будет выполняться после строки?

В вашем текущем кодексе нет. Хотя ваша функция doTheThing имеет асинхронную сигнатуру функции (т. Е. Она принимает обратный вызов в качестве последнего аргумента, который для аутсайдера, не знающего о реализации функции, предполагает, что он асинхронный), он фактически полностью синхронный, и callback будет вызван без уступания к времени выполнения.

Однако

Вы действительно не имеют никаких оснований, чтобы дать ваш doTheThing код асинхронной подпись, если вы не вмещающий для введения реального поведения асинхронной в doTheThing в какой-то момент. И в этот момент у вас есть проблема, потому что вызывается порядок, в котором вызывается foo и bar.

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

doTheThing = function(arg1){ 
    return null 
}; 
doTheThing() 
foo() 
bar() 

или изменить реализацию заглушки doTheThing непосредственно включить вызов setImmediate, т.е.

var doTheThing = function(arg1, callback){ 
    setImmediate(function() { callback();); 
}; 

Обратите внимание, что это также может быть записан как

var doTheThing = function(arg1, callback){ 
    setImmediate(callback); 
}; 

но это только потому, что в этот момент обратный вызов не принимает никаких аргументов. Первая версия ближе к тому, что у вас было.

Как только вы это сделаете, bar всегда будет вызываться до foo, и теперь стало безопасным вводить асинхронную функциональность в doTheThing.

+0

Re: «асинхронная сигнатура функции» - это соглашение node.js, что параметр с именем «обратный вызов» сигнализирует асинхронное поведение? –

+0

Я не уверен, что это можно считать конвенцией как таковой. Как правило, когда авторы библиотек раскрывают «синхронные» функциональные возможности (то есть что-то, что просто делает хруст), оно обычно подвергается синхронной сигнатуре, потому что это проще в использовании. Здравый смысл, на самом деле. Давайте сохраним это при этом * I * ожидаем, что обратный вызов будет вызван асинхронно, и не будет пропущен, если функция в конкретной библиотеке не будет, и мне как-то пришлось найти трудный путь. Я бы также опубликовал отчет об ошибке. IMO, хорошие заглушки для асинхронного кода используют 'setImmediate' или аналогичные. –

+0

Я не знаю никаких функций в ядре узла, которые нарушают это правило.Функции «sync» в ядре Node имеют синхронную сигнатуру (т. Е. Они просто возвращают результат). В этом смысле я думаю, что ядро ​​Node накладывает некоторые надежды на правильное поведение. –

Смежные вопросы