@Override
  public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter filter) {
    return new CheckAndMutateWithFilterBuilder() {

      private final CheckAndMutateWithFilterBuilder builder = rawTable.checkAndMutate(row, filter);

      @Override
      public CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange) {
        builder.timeRange(timeRange);
        return this;
      }

      @Override
      public CompletableFuture<Boolean> thenPut(Put put) {
        return wrap(builder.thenPut(put));
      }

      @Override
      public CompletableFuture<Boolean> thenDelete(Delete delete) {
        return wrap(builder.thenDelete(delete));
      }

      @Override
      public CompletableFuture<Boolean> thenMutate(RowMutations mutation) {
        return wrap(builder.thenMutate(mutation));
      }
    };
  }
Just a wrapper of {@link RawAsyncTableImpl}. The difference is that users need to provide a
thread pool when constructing this class, and the callback methods registered to the returned
{@link CompletableFuture} will be executed in this thread pool. So usually it is safe for users
to do anything they want in the callbacks without breaking the rpc framework.
hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java