Fire DeferredList بعد عدة استدعاءات

twisted.internet.defer.DeferredList يفعل هذا:

أقوم بدمج مجموعة من عمليات الإرجاء في معاودة اتصال واحدة.

     

أتتبع قائمة المؤهلين لاستدعاءاتهم ، وقم بإجراء واحدة   رد الاتصال عند الانتهاء من جميع ، قائمة (النجاح ، النتيجة)   tuples ، "success" كونها منطقية.

     

لاحظ أنه لا يزال بإمكانك استخدام المؤجل بعد وضعه في   DeferredList. على سبيل المثال ، يمكنك منع "خطأ غير معالج في   رسائل مؤجلة من خلال إضافة عمليات خدش إلى تأجيلات بعد الوضع   لهم في DeferredList ، كما لن تبتلع DeferredList الأخطاء.   (على الرغم من أن طريقة أكثر ملاءمة للقيام بذلك هي ببساطة لضبط   علم consumeErrors)

def __init__(self, deferredList, fireOnOneCallback=0, fireOnOneErrback=0, consumeErrors=0): (source)
    overrides twisted.internet.defer.Deferred.__init__
    Initialize a DeferredList.
    Parameters  deferredList    The list of deferreds to track. (type: list of Deferreds )
    fireOnOneCallback   (keyword param) a flag indicating that only one callback needs to be fired for me to call my callback
    fireOnOneErrback    (keyword param) a flag indicating that only one errback needs to be fired for me to call my errback
    consumeErrors   (keyword param) a flag indicating that any errors raised in the original deferreds should be consumed by this DeferredList. This is useful to prevent spurious warnings being logged.

على وجه التحديد:

fireOnOneCallback (param param) علامة تشير إلى أن واحدة فقط   يجب أن يتم الاتصال بي لاستدعاء معاودة الاتصال

إنني أسعى إلى سلوك مثل fireOnOneCallback = True ، ولكن بدلاً من ذلك ، يمكنك إطلاق على معاودة الاتصال. حاولت القيام بذلك ، لكنه تحول إلى فوضى بالفعل. أنا متأكد من أن هناك طريقة أفضل.

def _get_fired_index(deferred_list):
    for index, (success, value) in enumerate(deferred_list):
        if success:
            return index
    raise ValueError('No deferreds were fired.')


def _fire_on_other_callback(already_fired_index, deferred_list, callback, ):
    dlist_except_first_fired = (
        deferred_list[:already_fired_index]
        + deferred_list[already_fired_index + 1:]
    )
    dlist2 = DeferredList(dlist_except_first_fired, fireOnOneCallback=True)
    dlist2.addCallback(callback, deferred_list)


def _fire_on_two_callbacks(deferreds, callback, errback):
    dlist1 = DeferredList(deferreds, fireOnOneCallback=True)
    dlist1.addCallback(_get_fired_index)
    dlist1.addCallback(_fire_on_other_callback, deferreds, callback, errback)
0

2 إجابة

وإليك نهج واحد ممكن.

from __future__ import print_function

import attr
from twisted.internet.defer import Deferred

def fireOnN(n, ds):
    acc = _Accumulator(n)
    for index, d in enumerate(ds):
        d.addCallback(acc.one_result, index)
    return acc.n_results

@attr.s
class _Accumulator(object):
    n = attr.ib()
    so_far = attr.ib(default=attr.Factory(dict))
    done = attr.ib(default=False)
    n_results = attr.ib(default=attr.Factory(Deferred))

    def one_result(self, result, index):
        if self.done:
            return result
        self.so_far[index] = result
        if len(self.so_far) == self.n:
            self.done = True
            so_far = self.so_far
            self.so_far = None
            self.n_results.callback(so_far)

dx = list(Deferred().addCallback(print, i) for i in range(3))
done = fireOnN(2, dx)
done.addCallback(print, "done")

for i, d in enumerate(dx):
    d.callback("result {}".format(i))

لاحظ أن هذا التنفيذ لا يتعامل مع عمليات التراجع وربما يكون هناك أوجه قصور أخرى (مثل التمسك بمرجع n_results ). ومع ذلك ، فإن الفكرة الأساسية هي الصوت: تجميع الحالة من الاستدعاءات حتى يتحقق الشرط المطلوب ثم إطلاق آخر مؤجل.

DeferredList only brings unnecessary complexity to this problem with its unrelated features and interface not designed for solving this problem.

0
وأضاف

إليك طريقة أخرى تستخدم DeferredSemaphore للتعامل مع حالة السباق المحتملة. سيطلق هذا بمجرد تشغيل n من المؤجلات وإلغاء الباقي.

from twisted.internet import defer


def fireAfterNthCallback(deferreds, n):
    if not n or n > len(deferreds):
        raise ValueError

    results = {}
    finished_deferred = defer.Deferred()
    sem = defer.DeferredSemaphore(1)

    def wrap_sem(result, index):
        return sem.run(callback_result, result, index)

    def cancel_remaining():
        finished = [deferreds[index] for index in results.keys()]
        for d in finished:
            deferreds.remove(d)
        for d in deferreds:
            d.addErrback(lambda err: err.trap(defer.CancelledError))
            d.cancel()

    def callback_result(result, index):
        results[index] = result
        if len(results) >= n:
            cancel_remaining()
            finished_deferred.callback(results.values())
        return result

    for deferred_index, deferred in enumerate(deferreds):
        deferred.addCallback(wrap_sem, deferred_index)

    return finished_deferred
0
وأضاف