简体   繁体   中英

Asynchronous flow of execution in Java

I'm writing a method where I want to query a database and perform another method after the query has finished. Since the call to the database is asynchronous, though, I can't seem to figure out how to stop the main flow of execution until the database call is complete, like so:

public void getUserFromEmail(String email) {
    Firebase usersRef = rootRef.child("users");

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            System.out.println(firebaseError.getMessage());
        }
    });
    //Perform some more operations after database call is finished
}

I'm used to working with asynchronous callbacks in Nodejs, but I can't figure out if there's any equivalent way of controlling execution flow in Java. Is there any easy way to achieve what I want to do?

You can't stop the main flow, the main thread will continue on. (Unless you otherwise stop it, but that will cause "Application Not Responding", so don't).

Just do the action in the callback.

public void getUserFromEmail(String email) {
    Firebase usersRef = rootRef.child("users");

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // Perform some more operations after database call is finished
            // OR... callSomeMethod();
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            System.out.println(firebaseError.getMessage());
        }
    });

}

public void callSomeMethod() {
    // Perform some more operations after database call is finished
}

If you come from a Node background, then maybe this looks similar. Passing along the function to getUserFromEmail

public void otherMethod() {
    getUserFromEmail("steve@example.com", new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // Perform some more operations after database call is finished
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            System.out.println(firebaseError.getMessage());
        }
    });
}

public void getUserFromEmail(String email, ValueEventListener listener) {
    rootRef.child("users")
        .orderByKey()
        .equalTo(email)
        .addListenerForSingleValueEvent(listener);
}

You can control the flow if you really want to using locks. For instance using CountdownLatch :

public DataSnapshot getUserFromEmail(String email) throws InterruptedException {
    AtomicReference<DataSnapshot> dataSnapshotRef = new AtomicReference<DataSnapshot>();
    CountDownLatch completeSignal = new CountDownLatch(1);

    Firebase usersRef = rootRef.child("users");

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            dataSnapshotRef.set(dataSnapshot);
            // Causes execution of the waiting thread to continue
            // from the `await` call
            completeSignal.countDown();
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            completeSignal.countDown();
        }
    });

    // Execution on this thread pauses at the `await` call until
    // countDown has been called from another thread.
    completeSignal.await();

    // Do stuff after onDataChanged or onCancelled has been called
    DataSnapshot dataSnapshot = dataSnapshotRef.get();
}

However this approach will block the thread that executed getUserFromEmail until Firebase has returned some data. Particularly, if you call getUserFromEmail from your UI thread then this will block the UI thread and cause an ANR.

It is better to follow the callback approach like so:

public DataSnapshot getUserFromEmail(String email, ValueEventListener listener) {
    Firebase usersRef = rootRef.child("users");

    //Asynchronous database call, want to stop everything else 
    usersRef.orderByKey().equalTo(email).addListenerForSingleValueEvent(listener);
}

public void getUserAndThenDoSomething() {
    getUserFromEmail("bob@example.com", new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // Do stuff after getting the email here
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM