Skip to content

Instantly share code, notes, and snippets.

@RyoJerryYu
Last active June 12, 2021 04:46
Show Gist options
  • Save RyoJerryYu/77f76cd56ccef734fc9749a2976669be to your computer and use it in GitHub Desktop.
Save RyoJerryYu/77f76cd56ccef734fc9749a2976669be to your computer and use it in GitHub Desktop.
A class that help for organize ordered works that full of callbacks.
/**
* A class that help for organizing async works.
*
* @author RyoJerryYu
* @date 2021/6/11 9:14
*/
public class AsyncWorkflow {
Queue<AsyncWork> queue;
public enum State {PREPARING, EXECUTING, FINISHED}
State state;
public static AsyncWorkflowBuilder getBuilder() {
return new AsyncWorkflowBuilder();
}
/**
* Private constructor, that only allow creating instance
* by Builder.
*
* @param queue A queue that contains async work nodes to execute in order.
*/
private AsyncWorkflow(Queue<AsyncWork> queue) {
this.queue = queue;
state = State.PREPARING;
}
/**
* Called in a work nodes that execute next step in workflow.
*/
public void next() {
if (state != State.EXECUTING) {
throw new IllegalStateException("Trying to execute next work while workflow not running.");
}
AsyncWork work = queue.poll();
if (work == null) {
finish();
return;
}
work.execute(this);
}
/**
* Called in a work nodes that interrupt the workflow.
*/
public void interrupt() {
finish();
}
private void start() {
if (state != State.PREPARING) {
throw new IllegalStateException("Running a workflow while preparing.");
}
state = State.EXECUTING;
next();
}
private void finish() {
queue.clear();
state = State.FINISHED;
}
/**
* Builder Class
* {@link AsyncWorkflow} could only be constucted here.
*
* There is no any other way to return a workflow that
* public methods of workflow could only be called in a work node.
*/
public static class AsyncWorkflowBuilder {
Queue<AsyncWork> queue;
public AsyncWorkflowBuilder() {
queue = new LinkedBlockingQueue<>();
}
/**
* Add a work node to workflow.
*
* @param work: work node
* @return this: return itself for chain calling.
*/
public AsyncWorkflowBuilder append(AsyncWork work) {
queue.add(work);
return this;
}
/**
* Construct and start the workflow.
*/
public void run() {
AsyncWorkflow chain = new AsyncWorkflow(queue);
chain.start();
}
}
public interface AsyncWork {
/**
* Interface of async work node.
*
* {@link AsyncWorkflow#next()} or {@link AsyncWorkflow#interrupt()}
* should be called in the end of any brunch in work node.
*
* @param workflow: The workflow that execute the work.
*/
void execute(AsyncWorkflow workflow);
}
}

Introducing:

I was working on a project that full of callbacks in codes, and the project is so complicated so that I can't refactor the basic structure.

So I created this class to help myself for organize async works, that should be execute in order but can not be decoupled because of callbacks should be execute inside the works.

在工作中我需要接受一个项目, 在这个项目里有许多异步操作, 而这些异步操作 都通过回调函数来实现. 因为这个项目比较复杂,我无法在短时间内去重构 这些基础结构.

而在这项目里我需要创建一些需要按顺序执行的步骤, 但这些步骤里有异步操作, 需要在执行回调函数时才能去调用下一步, 导致无法将这些步骤相互解耦. 因此我创建了这个类,来帮我管理这些工作步骤.

Usage:

Create your async work as follow, receiving a AsyncWorkflow parameter so that you can call workflow.next() or workflow.interrupt() when this work finished or failed.

void anAsyncWork(AsyncWorkflow workflow, Object otherParam) {
  doSomething();
  doAsyncOperation(new Callback() {
      if(isFailed) {
          workflow.interrupt();
      }
      doAfterAsyncOperation();
      workflow.next()
  })
}

And then you could organize and execute your works like this.

void executeAsyncWorks() {
  AsyncWorkflow
      .getBuilder()
      .append((workflow)-> anAsyncWork(workflow, otherParam))
      .append(this::workWithoutOtherParam)
      .run()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment