Странный порядок выполнения обратного вызова в Twisted? - PullRequest
3 голосов
/ 22 ноября 2011

Рассмотрим следующий код:

from twisted.internet.defer import Deferred

d1 = Deferred()
d2 = Deferred()

def f1(result):
    print 'f1',

def f2(result):
    print 'f2',

def f3(result):
    print 'f3',

def fd(result):
    return d2

d1.addCallback(f1)
d1.addCallback(fd)
d1.addCallback(f3)

#/BLOCK====
d2.addCallback(f2)
d1.callback(None)
#=======BLOCK/

d2.callback(None)

Это выводит то, что я ожидал:

f1 f2 f3

Однако, когда я меняю порядок операторов в BLOCK на

#/BLOCK====
d1.callback(None)
d2.addCallback(f2)
#=======BLOCK/

т.е. Запустив d1 перед добавлением обратного вызова к d2, я получаю:

f1 f3 f2

Я не понимаю, почему время срабатывания отложений должно влиять на порядок выполнения обратного вызова. Это проблема с Twisted или это имеет какой-то смысл?

1 Ответ

3 голосов
/ 22 ноября 2011

tl; dr - Когда вы возвращаете отложенный (d2) из обратного вызова (fd), он вставляется в цепочку обратного вызова любого отложенного (d1), называемого fd,Это делается путем добавления продолжения цепочки обратных вызовов d1 в качестве обратного вызова на d2, поэтому, если вы добавите обратный вызов к d2 после срабатывания d1, он будет привязан к после продолжения d1.


Я думаю, что вам не хватает того факта, что отложенные функции не являются асинхронными сами по себе, они просто более структурированыспособ цепочки обратных вызовов и обработчиков ошибок в асинхронном коде.Обратные вызовы не вызываются в неуказанное позднее время, они называются , когда запускается отложенный .Если вы хотите, чтобы это было позже, вам нужно интегрировать ваши звонки с reactor.

Так что, возможно, это делает ответ на этот вопрос немного более очевидным:

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

Это произойдет, если вы измените обратные вызовы после , и вы запустите отложенное.

Объяснение вашего кода

В первом примере:

  1. d1.callback(None) вызывает запуск d1
  2. f1 вызывается (печатает "f1")
  3. fd вызывается (возвращает d2)

Теперь все останавливается, потому что d2 было вставлено в цепочку обратных вызовов для d1 между f1 и f3, но еще не было произведено.Тогда ...

  1. d2.callback(None) заставляет d2 быть запущенным
  2. f2 вызывается (печатает "f2")
  3. d1 's обратная связь возобновляется;поэтому f3 вызывается (печатает "f3")

Во втором примере

  1. d1.callback(None) вызывает запуск d1
  2. f1 вызывается (печатает "f1")
  3. fd вызывается (возвращает d2)

Здесь d2 снова вставляется в цепочку обратного вызова.Это делается путем добавления продолжения текущей цепочки обратных вызовов в качестве обратного вызова к d2.Поэтому, даже если вы явно добавили f2 в качестве обратного вызова для d2, он добавляется после продолжения цепочки обратных вызовов d1.Следовательно ...

  1. d2.callback(None) вызывает увольнение d2;это приводит к продолжению цепочки обратного вызова d1, начиная с ...
  2. f3 вызывается (печатает "f3")
  3. d2 следующий обратный вызов в это цепочка f2, поэтому ...
  4. f2 называется (печатает "f2")
...