docs: @Nullable
    public static <T> T waitForFinalResult(DataSource<T> dataSource) throws Throwable {
      final CountDownLatch latch = new CountDownLatch(1);
      final ValueHolder<T> resultHolder = new ValueHolder<>();
      final ValueHolder<Throwable> pendingException = new ValueHolder<>();
  
      dataSource.subscribe(
          new DataSubscriber<T>() {
            @Override
            public void onNewResult(DataSource<T> dataSource) {
              // only return the final result
              if (!dataSource.isFinished()) {
                return;
              }
  
              try {
                resultHolder.value = dataSource.getResult();
              } finally {
                latch.countDown();
              }
            }
  
            @Override
            public void onFailure(DataSource<T> dataSource) {
              try {
                pendingException.value = dataSource.getFailureCause();
              } finally {
                latch.countDown();
              }
            }
  
            @Override
            public void onCancellation(DataSource<T> dataSource) {
              // will make the outer method return null
              latch.countDown();
            }
  
            @Override
            public void onProgressUpdate(DataSource<T> dataSource) {
              // intentionally left blank
            }
          },
          new Executor() {
            @Override
            public void execute(Runnable command) {
              // it's fair to run these short methods on the datasource thread to avoid
              // context switching
              command.run();
            }
          });
  
      // wait for countdown() by the DataSubscriber
      latch.await();
  
      // if the data source failed, throw its exception
      if (pendingException.value != null) {
        throw pendingException.value;
      }
  
      return resultHolder.value;
    }