code: def submit_async_work_chain(self, work_chain, workunit_parent, done_hook=None):
      
      def done():
        if done_hook:
          done_hook()
        with self._pending_workchains_cond:
          self._pending_workchains -= 1
          self._pending_workchains_cond.notify()
  
      def error(e):
        done()
        self._run_tracker.log(Report.ERROR, '{}'.format(e))
  
      # We filter out Nones defensively. There shouldn't be any, but if a bug causes one,
      # Pants might hang indefinitely without this filtering.
      work_iter = (_f for _f in work_chain if _f)
  
      def submit_next():
        try:
          self.submit_async_work(next(work_iter), workunit_parent=workunit_parent,
                                 on_success=lambda x: submit_next(), on_failure=error)
        except StopIteration:
          done()  # The success case.
  
      with self._pending_workchains_cond:
        self._pending_workchains += 1
      try:
        submit_next()
      except Exception as e:  # Handles errors in the submission code.
        done()
        self._run_tracker.log(Report.ERROR, '{}'.format(e))
        raise
docs: Submit work to be executed in the background.
  
      - work_chain: An iterable of Work instances. Will be invoked serially. Each instance may
                    have a different cardinality. There is no output-input chaining: the argument
                    tuples must already be present in each work instance.  If any work throws an
                    exception no subsequent work in the chain will be attempted.
      - workunit_parent: Work is accounted for under this workunit.
      - done_hook: If not None, invoked with no args after all work is done, or on error.