Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my function that calls an API return an empty or null value?

(Disclaimer: There are a ton of questions which arise from people asking about data being null/incorrect when using asynchronous operations through requests such as facebook,firebase, etc. My intention for this question was to provide a simple answer for that problem to everyone starting out with asynchronous operations in android)

I'm trying to get data from one of my operations, when I debug it using breakpoints or logs, the values are there, but when I run it they are always null, how can I solve this ?

Firebase

firebaseFirestore.collection("some collection").get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot documentSnapshots) {
                     //I want to return these values I receive here? 
            })

Facebook

GraphRequest request = GraphRequest.newGraphPathRequest(
            accessToken,
            "some path",
            new GraphRequest.Callback() {
                @Override
                public void onCompleted(GraphResponse response) {
                     //I want to return these values I receive here? 
                }
            });
    request.executeAsync();

Etc.

like image 741
a_local_nobody Avatar asked Aug 02 '19 16:08

a_local_nobody


People also ask

What happens if you send a null value in an API call?

If a "NULL" is passed in the JSON, we get NULL in the object, but any other field that is not passed is NULL as well. Therefore we cannot distinguish which field can be deleted and which field cannot be touched. The complete object is sent.

Should you return null or empty object?

Returning null is usually the best idea if you intend to indicate that no data is available. An empty object implies data has been returned, whereas returning null clearly indicates that nothing has been returned.

What is the meaning of return null?

null return means a return which indicates that no transaction was made by the registered person during the tax period and no amount of tax is to be paid or refunded. Sample 1.

Can we return null in C#?

This means that you can put any object in a collection because all classes in the C# programming language extend from the object base class. Also, we cannot simply return null from a generic method like in normal method.


2 Answers

What is a Synchronous/Asynchronous operation ?

Well, Synchronous waits until the task has completed. Your code executes "top-down" in this situation.

Asynchronous completes a task in the background and can notify you when it is complete.

If you want to return the values from an async operation through a method/function, you can define your own callbacks in your method/function to use these values as they are returned from these operations.

Here's how for Java

Start off by defining an interface :

interface Callback {
    void myResponseCallback(YourReturnType result);//whatever your return type is: string, integer, etc.
}

next, change your method signature to be like this :

public void foo(final Callback callback) { // make your method, which was previously returning something, return void, and add in the new callback interface.

next up, wherever you previously wanted to use those values, add this line :

callback.myResponseCallback(yourResponseObject);

as an example :

@Override
public void onSuccess(QuerySnapshot documentSnapshots) {
    // create your object you want to return here
    String bar = document.get("something").toString();
    callback.myResponseCallback(bar);
})

now, where you were previously calling your method called foo:

foo(new Callback() {
        @Override
        public void myResponseCallback(YourReturnType result) {
            //here, this result parameter that comes through is your api call result to use, so use this result right here to do any operation you previously wanted to do. 
        }
    });
}

How do you do this for Kotlin ? (as a basic example where you only care for a single result)

start off by changing your method signature to something like this:

fun foo(callback:(YourReturnType) -> Unit) {
.....

then, inside your asynchronous operation's result :

firestore.collection("something")
         .document("document").get()
         .addOnSuccessListener { 
             val bar = it.get("something").toString()
             callback(bar)
         }

then, where you would have previously called your method called foo, you now do this :

foo() { result->
    // here, this result parameter that comes through is 
    // whatever you passed to the callback in the code aboce, 
    // so use this result right here to do any operation 
    // you previously wanted to do. 
}
// Be aware that code outside the callback here will run
// BEFORE the code above, and cannot rely on any data that may
// be set inside the callback.

if your foo method previously took in parameters :

fun foo(value:SomeType, callback:(YourType) -> Unit)

you simply change it to :

foo(yourValueHere) { result ->
    // here, this result parameter that comes through is 
    // whatever you passed to the callback in the code aboce, 
    // so use this result right here to do any operation 
    // you previously wanted to do. 
}

these solutions show how you can create a method/function to return values from async operations you've performed through the use of callbacks.


However, it is important to understand that, should you not be interested in creating a method/function for these:

@Override
public void onSuccess(SomeApiObjectType someApiResult) {
    // here, this `onSuccess` callback provided by the api 
    // already has the data you're looking for (in this example, 
    // that data would be `someApiResult`).
    // you can simply add all your relevant code which would 
    // be using this result inside this block here, this will 
    // include any manipulation of data, populating adapters, etc. 
    // this is the only place where you will have access to the
    // data returned by the api call, assuming your api follows
    // this pattern
})
like image 88
a_local_nobody Avatar answered Oct 28 '22 06:10

a_local_nobody


There's a particular pattern of this nature I've seen repeatedly, and I think an explanation of what's happening would help. The pattern is a function/method that calls an API, assigning the result to a variable in the callback, and returns that variable.

The following function/method always returns null, even if the result from the API is not null.

Kotlin

fun foo(): String? {
   var myReturnValue: String? = null
   someApi.addOnSuccessListener { result ->
       myReturnValue = result.value
   }.execute()
   return myReturnValue
}

Kotlin coroutine

fun foo(): String? {
   var myReturnValue: String? = null
   lifecycleScope.launch { 
       myReturnValue = someApiSuspendFunction()
   }
   return myReturnValue
}

Java 8

private String fooValue = null;

private String foo() {
    someApi.addOnSuccessListener(result -> fooValue = result.getValue())
        .execute();
    return fooValue;
}

Java 7

private String fooValue = null;

private String foo() {
    someApi.addOnSuccessListener(new OnSuccessListener<String>() {
        public void onSuccess(Result<String> result) {
            fooValue = result.getValue();
        }
    }).execute();
    return fooValue;
}

The reason is that when you pass a callback or listener to an API function, that callback code will only be run some time in the future, when the API is done with its work. By passing the callback to the API function, you are queuing up work, but the current function (foo() in this case) returns immediately before that work begins and before that callback code is run.

Or in the case of the coroutine example above, the launched coroutine is very unlikely to complete before the function that started it.

Your function that calls the API cannot return the result that is returned in the callback (unless it's a Kotlin coroutine suspend function). The solution, explained in the other answer, is to make your own function take a callback parameter and not return anything.

Alternatively, if you're working with coroutines, you can make your function suspend instead of launching a separate coroutine. When you have suspend functions, somewhere in your code you must launch a coroutine and handle the results within the coroutine. Typically, you would launch a coroutine in a lifecycle function like onCreate(), or in a UI callback like in an OnClickListener.

like image 26
Tenfour04 Avatar answered Oct 28 '22 06:10

Tenfour04