4

I have a function that works absolutely fine. I just want to return true or false depending on the promise.

 //I want this function to return a simple true or false!!!
function isAppOnline() {
            var is_connected = connectivityMonitor.isInternetConnected();
            is_connected.then(function(result) {
                console.log('INTERNET_CHECK_API : app online');//works fine
                return true;//this is not being returned
            }, function(error) {
                console.log('INTERNET_CHECK_API : app offline');//works fine
                return false;//this is not being returned
            });
        }

But when I call this function,

is_online = isAppOnline();

is_online is always undefined . Why is the function not able to return a simple boolean value?

Update :

This is what I am trying to do : I simply want to open a popup which notifies the user that he is offline. I am calling the function isAppOnline periodically after 10secs. This function is already using a promise in my factories . I dont want to overcomplicate things but its important to me that this function returns a boolean so based on this,I can take my actions accordingly.

HIRA THAKUR
  • 15,044
  • 14
  • 47
  • 82
  • Based on your edit, you could strip this popup logic out into its own controller, probably a good idea anyhow. This controller could sit with other static content between pages, is your nav in its own controller it could live at a similar level in the app to that? then you only have to use the promise in one place, plus it is more modular. Single cause for concern. Also makes it more testable. you could even put it in a directive and just stick it in your view where you want it. – ste2425 Oct 20 '15 at 07:43
  • this code is for all the controllers. :) – HIRA THAKUR Oct 20 '15 at 07:55

4 Answers4

8

EDIT: For ES2017 If your one of the lucky souls who gets to use ES2017 then you can use the new await/async keywords. They are pretty brilliant and allow you to write async code that reads synchronous. (It is still promises under the hood, just unboxing of them).

function isOnline() {
    return Promise.resolve(true);
}

async function Main() {
    const online = await isOnline();

  console.log(online);
}

Main();

fidle


Because it is asynchronous. Your isAppOnline method returns before your promise has resolved.

I presume is making some form of AJAX call to check network connectivity so there it will have to wait for it to respond. JavaScript is single threaded, if that thread locked up waiting for that request to respond so it could be synchronous your whole JavaScript would pause until it returns. Not good.

So if you want the caller of isAppOnline to know the result you have to options. Either passing it a call back or return the promise (better option)

function isAppOnline(cb) {
    var is_connected = connectivityMonitor.isInternetConnected();
    is_connected.then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        cb(true);
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        cb(false);
    });
}

//better option
function isAppOnline() {
    return connectivityMonitor.isInternetConnected().then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        return true;
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        return false;
    });
}

//used as
isAppOnline().then(function (isOnline) {
    console.log('Is it online?', isOnline);
});
ste2425
  • 4,213
  • 2
  • 17
  • 32
  • Oh which, the second? I should add without seeing the rest of your code this specific implementation is untested. However the principle works fine, i have used this method personally in nearly every angular app ive worked on. Returning promises from methods that use `.then()` is perfectly chain-able. – ste2425 Oct 20 '15 at 07:34
  • yup,the second one. well I was looking for something simpler . I knew i could add one more promise, but then thats the reason why I asked the question. Promise after promise after promise .. isnt there something more simpler. Anyways thx. – HIRA THAKUR Oct 20 '15 at 07:37
  • 2
    @Jayesh Jain Note the return added before is_connected – Icycool Oct 20 '15 at 07:37
  • @lcycool Answer edited. JayeshJain. If you having to do promise after promise within nested promise chains, that have inceptually chained from promises that have not been created yet, you may need to reconsider your application structure. – ste2425 Oct 20 '15 at 07:38
  • well its just one promise in my factory .. and then isAppOnline() in my ```.run()``` . so there are two promises. I dont think i have a bad structure,i may need to use the same function at multiple places. – HIRA THAKUR Oct 20 '15 at 07:53
  • Functions that returns a promise is self-contained - you don't need to care about caller, which is good if you need to call it in different places. – Icycool Oct 20 '15 at 09:16
1

Promise works differently with simple statements because they might return later, so you might need to rethink the flow of your program to handle different initial and final states.

function isAppOnline() {
    var is_connected = connectivityMonitor.isInternetConnected();
    return is_connected.then(function(result) { // add return here to return the whole promise
        console.log('INTERNET_CHECK_API : app online');//works fine
        return; //this will resolve the promise returned
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        $q.reject(); // this will reject the promise returned
    });
}

You will have to handle changing states in your calling controller (it might be false for a short while then turned to true, etc). You might consider using a loading state to prevent misdirecting users.

is_online = false;
isLoading = true;

isAppOnline().then(function() {
    is_online = true;
}, function() {
    is_online = false;
})
.finally(function(){
    isLoading = false;
});
Icycool
  • 6,535
  • 1
  • 20
  • 30
0

Your question indicates a pretty deep need for further exploration of Javascript's asynchronous processing. I recommend reading: How do I return the response from an asynchronous call?

There are hacks you can implement to force a method to return synchronously, but angular is built in a way that honors promises almost everywhere. If you could give us some more context on what you're trying to accomplish we may be able to help you write your code in a way that takes advantage of the promise itself.

Community
  • 1
  • 1
David Boskovic
  • 1,430
  • 9
  • 14
-1

You could try the following (this is untested code since you didn't provide a working script):

function isAppOnline() {
    var defer = $q.defer();

    var is_connected = connectivityMonitor.isInternetConnected();
    is_connected.then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        defer.resolve(true);
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        defer.resolve(false);
    });

    return defer.promise;
}

And call it like this:

var is_online = false;
isAppOnline().then(function(data){
    is_online = data;
});

Or, pass the is_connected object directly, than you do better error handling:

function isAppOnline() {
    var defer = $q.defer();

    var is_connected = connectivityMonitor.isInternetConnected();
    is_connected.then(function(result) {
        console.log('INTERNET_CHECK_API : app online');//works fine
        defer.resolve(is_connected);
    }, function(error) {
        console.log('INTERNET_CHECK_API : app offline');//works fine
        defer.reject();
    });

    return defer.promise;
}
Spikee
  • 3,381
  • 6
  • 25
  • 58
  • 1
    Please refer to my answer or ste2425's on returning a promise rather than create a new deferred. – Icycool Oct 20 '15 at 07:30
  • Well that way it's ready for a more complex return object, in case you want to do something like this: var result = { isConnected: is_connected, ...}. In my experience it's best to do it this way because as your app grows, you often need to do it anyway, at some point, so better early. – Spikee Oct 20 '15 at 07:51
  • Nope. The case where you really need to create a deferred is when the core of the function doesn't return a promise, like $timeout. In fact creating a deferred when a promise can be returned is defined as one of the [promise-antipatterns](http://taoofcode.net/promise-anti-patterns/#the_forgotten_promise). – Icycool Oct 20 '15 at 09:12
  • So how do you solve adding to an existing promise result (just curious)? – Spikee Oct 20 '15 at 09:53
  • `function f1 = function (p1) { return f2.then(function(res2) { return p1 + res2; }) }` And then in caller: `f1('p1').then(function(res1) { // res1 == 'p1' + res2 })` This is just showing the resolve route, reject route is almost the same using $q.reject. You can pretty much do anything to res2 before returning it as res1. – Icycool Oct 20 '15 at 09:59
  • Sorry I can't understand. Is the code hard to read or I didn't understand your question correctly? – Icycool Oct 21 '15 at 03:05
  • @Spikee i don't think your argument stands for a more complex return object. Say your returning the raw response from a http call by returning the response. So `return $http.get()` if you want to modify that return object just do `return $http.get().then(someFunctionThatModifiesIt)` What ever `someFunctionThatModifiesIt()` does to your data if you return it, it will be returned to your caller. You can chain as many promises you want before you return it out of your service to pre-process your data. – ste2425 Oct 21 '15 at 06:45
  • @Spikee sorry for the double comment but needed to say this. Returning the promise in your custom resolve in your second example just so your caller knows if an error is raised is very bad. Use `.catch()` in your service. If the error can't be recovered return `$q.reject(error)`. Then your callers `.catch()` be be called. Instead of `.then(function (p) { p.catch(); });` you just do `.then().catch()`; – ste2425 Oct 21 '15 at 06:52