链接一系列异步调用

时间:2014-09-26 12:13:02

标签: java asynchronous

我有一系列异步操作

private void doSomething(){
  get("/something", new Callback(){
    void onComplete(String data){
      updateUi(something, data);           
      doSomethingElse();
    }
  });
}

private void doSomethingElse(){
  get("/something/else", new Callback(){
    void onComplete(String data){
      updateUi(somethingElse, data);
      doYetAnotherThing();
    }
  });
}

private void doYetAnotherThing(){
  get("/yet/another/thing", new Callback(){
    void onComplete(String data){
      updateUi(yetAnotherThing, data);
      allDone();
    }
  });
}

这几乎没有问题:

  1. 无法在其他地方重复使用任何回调,因为每个回调都与"下一步"
  2. 有内在联系。
  3. 重新订购操作或插入其他操作是非直观的,涉及到处跳跃。
  4. 我已查看以下选项以缓解此问题:

    1. ExecuterService#invokeAll - 我不知道如何在不阻止的情况下使用此解决方案。
    2. RxJava - 如果可以的话,我宁愿避免在我的应用程序中进行这种范式转换!
    3. Guava' ListenableFutures及其transform方法。我在互联网坚果周围的几个地方看到了这一点,老实说我不知道​​这会如何解决我的问题。
    4. 所以,问题是:在Java中链接一系列异步调用的好方法是什么?寻找适用于Java 7的解决方案,因为我需要这个Android应用程序。

3 个答案:

答案 0 :(得分:4)

对于遇到此问题的实际意图和用例,肯定会涉及一些猜测。此外,还不完全清楚somethingsomethingElseyetAnotherThing是什么(它们来自哪里以及应该去哪里)。

但是,根据您提供的信息,以及answer by slartidan的补充(或者更确切地说是扩展或概括):您在那里描绘的这些虚拟调用之间的区别似乎是

  • 传递给String方法
  • get参数
  • 名为
  • Callback
  • 接下来执行哪种方法

您可以将这些部分分解出来:String参数和Callback可以作为参数传递给创建Callable的常规方法。可以通过将这些Callable对象以适当的顺序放入列表中来定义调用的顺序,并使用单线程执行程序服务执行它们。

正如您在本示例的main方法中所看到的,然后可以相当容易地配置调用序列:

import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ChainedAsyncTest {

    public static void main(String[] args) throws InterruptedException {
        ChainedAsyncTest t = new ChainedAsyncTest();
        ExecutorService e = Executors.newFixedThreadPool(1);
        e.invokeAll(Arrays.asList(
            t.call("/something", t.somethingCallback),
            t.call("/something/else", t.somethingElseCallback),
            t.call("/yet/another/thing", t.yetAnotherThingCallback),
            t.allDone()));
    }

    private Callback somethingCallback = new Callback() {
        @Override
        public void onComplete(String data) {
            updateUi("something", data);
        }
    };

    private Callback somethingElseCallback = new Callback() {
        @Override
        public void onComplete(String data) {
            updateUi("somethingElse", data);
        }
    };

    private Callback yetAnotherThingCallback = new Callback() {
        @Override
        public void onComplete(String data) {
            updateUi("yetAnotherThing", data);
        }
    };

    private Callable<Void> call(
        final String key, final Callback callback) {
        return new Callable<Void>() {
            @Override
            public Void call() {
                get(key, callback);
                return null;
            }
        };
    }

    private Callable<Void> allDone() {
        return new Callable<Void>() {
            @Override
            public Void call() {
                System.out.println("allDone");
                return null;
            }
        };
    }



    interface Callback
    {
        void onComplete(String data);
    }
    private void get(String string, Callback callback) {
        System.out.println("Get "+string);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        callback.onComplete("result of "+string);
    }
    private void updateUi(String string, String data) {
        System.out.println("UpdateUI of "+string+" with "+data);
    }

}

(该示例使用invokeAll,它会阻塞直到所有任务都已执行。这可以通过不同方式解决,以便在呼叫站点真正无阻塞。主要思想是创建任务列表,这些都是通过相同的方法调用创建的)

答案 1 :(得分:1)

狡猾的想法:您可以将链式调用定义为方法参数,以使您的方法可重用。这是我的示例代码:

public class Scribble {

    final Callback step1 = new Callback() {
        void onComplete(String string) {
            doSomethingElse(step2);
        };
    };

    final Callback step2 = new Callback() {
        void onComplete(String string) {
            doYetAnotherThing(step3);
        };
    };

    final Callback step3 = new Callback() {
        void onComplete(String string) {
            allDone();
        }
    };

    private void start() {
        doSomething(step1);
    }

    private void doSomething(final Callback externalCallback) {
        get("/something", new Callback() {
            void onComplete(String data) {
                updateUi(something, data);
                externalCallback.onComplete(data);
            }
        });
    }

    private void doSomethingElse(final Callback externalCallback) {
        get("/something/else", new Callback() {
            void onComplete(String data) {
                updateUi(somethingElse, data);
                externalCallback.onComplete(data);
            }
        });
    }

    private void doYetAnotherThing(final Callback externalCallback) {
        get("/yet/another/thing", new Callback() {
            void onComplete(String data) {
                updateUi(yetAnotherThing, data);
                externalCallback.onComplete(data);
            }
        });
    }

    // - the code below is only to make everything compilable -

    public class Callback {

        void onComplete(String string) {
        }

    }

    private Object something;
    protected Object somethingElse;
    protected Object yetAnotherThing;

    protected void allDone() {
        System.out.println("Scribble.allDone()");
    }

    protected void updateUi(Object yetAnotherThing2, String data) {
        System.out.println("Scribble.updateUi()"+data);
    }

    private void get(String string, Callback callback) {
        System.out.println("get "+string);
        callback.onComplete(string);
    }

    public static void main(String[] args) {
        new Scribble().start();
    }

}

答案 2 :(得分:1)

我完全支持已批准的答案,但是当我开始在异步操作链中添加条件逻辑时,我也会为这些类型的问题创建一些东西。我最近把它发酵成一个简单的库(jasync-driver)。

以下是您如何连接示例的方法。如您所见,每个任务都不了解后面的任务。与批准的答案相反,任务的链接是通过简单的同步(...查找)方法体而不是列表来完成的。

public void doChainedLogic() {

    final AsyncTask<Void, Void> doSomething = new AsyncTask<Void, Void>() {
        @Override
        public void run(Void arg, final ResultHandler<Void> resultHandler) {
            get("/something", new Callback() {
                public void onComplete(String data) {
                    updateUi(something, data);
                    resultHandler.reportComplete();
                }
            });
        }
    };

    final AsyncTask<Void, Void> doSomethingElse = new AsyncTask<Void, Void>() {
        @Override
        public void run(Void arg, final ResultHandler<Void> resultHandler) {
            get("/something/else", new Callback() {
                public void onComplete(String data) {
                    updateUi(somethingElse, data);
                    resultHandler.reportComplete();
                }
            });
        }
    };

    final AsyncTask<Void, Void> doYetAnotherThing = new AsyncTask<Void, Void>() {
        @Override
        public void run(Void arg, final ResultHandler<Void> resultHandler) {
            get("/yet/another/thing", new Callback() {
                public void onComplete(String data) {
                    updateUi(yetAnotherThing, data);
                    resultHandler.reportComplete();
                }
            });
        }
    };

    // This looks synchronous, but behind the scenes JasyncDriver is
    // re-executing the body and skipping items already executed.
    final JasyncDriver driver = new JasyncDriver();
    driver.execute(new DriverBody() {
        public void run() {
            driver.execute(doSomething);
            driver.execute(doSomethingElse);
            driver.execute(doYetAnotherThing);
        }
    });
}

现在,这是对示例的一个调整,其中包括一些取决于异步结果的条件逻辑:

final AsyncTask<Void, String> checkSomething = new AsyncTask<Void, String>() {
    @Override
    public void run(Void arg, final ResultHandler<String> resultHandler) {
        get("/check/something", new Callback() {
            public void onComplete(String data) {
                resultHandler.reportComplete(data);
            }
        });
    }
};

final JasyncDriver driver = new JasyncDriver();
driver.execute(new DriverBody() {
    public void run() {
        driver.execute(doSomething);
        if ("foobar".equals(driver.execute(checkSomething))) {
            driver.execute(doSomethingElse);
        }
        driver.execute(doYetAnotherThing);
    }
});

如您所见,异步条件逻辑就像编写标准if语句一样简单。