Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJs: Have method return synchronously when it calls $http or $resource internally

Is there a way to wait on a promise so that you can get the actual result from it and return that instead of returning the promise itself? I'm thinking of something similar to how the C# await keyword works with Tasks.

Here is an example of why I'd like to have a method like canAccess() that returns true or false instead of a promise so that it can be used in an if statement. The method canAccess() would make an AJAX call using $http or $resource and then somehow wait for the promise to get resolved.

The would look something like this:

$scope.canAccess = function(page) {
    var resource = $resource('/api/access/:page');
    var result = resource.get({page: page});

    // how to await this and not return the promise but the real value
    return result.canAccess;
}

Is there anyway to do this?

like image 738
Jeff Treuting Avatar asked Nov 25 '13 03:11

Jeff Treuting


4 Answers

In general that's a bad idea. Let me tell you why. JavaScript in a browser is basically a single threaded beast. Come to think of it, it's single threaded in Node.js too. So anything you do to not "return" at the point you start waiting for the remote request to succeed or fail will likely involve some sort of looping to delay execution of the code after the request. Something like this:

var semaphore = false;
var superImportantInfo = null;

// Make a remote request.
$http.get('some wonderful URL for a service').then(function (results) {
  superImportantInfo = results;

  semaphore = true;
});

while (!semaphore) {
  // We're just waiting.
}

// Code we're trying to avoid running until we know the results of the URL call.
console.log('The thing I want for lunch is... " + superImportantInfo);

But if you try that in a browser and the call takes a long time, the browser will think your JavaScript code is stuck in a loop and pop up a message in the user's face giving the user the chance to stop your code. JavaScript therefore structures it like so:

// Make a remote request.
$http.get('some wonderful URL for a service').then(function (results) {
  // Code we're trying to avoid running until we know the results of the URL call.
  console.log('The thing I want for lunch is... " + results);
});

// Continue on with other code which does not need the super important info or 
// simply end our JavaScript altogether. The code inside the callback will be 
// executed later.

The idea being that the code in the callback will be triggered by an event whenever the service call returns. Because event driven is how JavaScript likes it. Timers in JavaScript are events, user actions are events, HTTP/HTTPS calls to send and receive data generate events too. And you're expected to structure your code to respond to those events when they come.

Can you not structure your code such that it thinks canAccess is false until such time as the remote service call returns and it maybe finds out that it really is true after all? I do that all the time in AngularJS code where I don't know what the ultimate set of permissions I should show to the user is because I haven't received them yet or I haven't received all of the data to display in the page at first. I have defaults which show until the real data comes back and then the page adjusts to its new form based on the new data. The two way binding of AngularJS makes that really quite easy.

like image 150
John Munsch Avatar answered Oct 17 '22 23:10

John Munsch


Use a .get() callback function to ensure you get a resolved resource.

Helpful links:

  • Official docs
  • How to add call back for $resource methods in AngularJS
like image 41
Shomz Avatar answered Oct 17 '22 23:10

Shomz


My approach was create a function with OLD javascript objects as follows:

var globalRequestSync = function (pUrl, pVerbo, pCallBack) {

        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = function () {

            if (httpRequest.readyState == 4 && httpRequest.status == 200) {
                pCallBack(httpRequest.responseText);
            }
        }
        httpRequest.open(pVerbo, pUrl, false);
        httpRequest.send(null);

    };
like image 1
RolandoCC Avatar answered Oct 18 '22 00:10

RolandoCC


You can't - there aren't any features in angular, Q (promises) or javascript (at this point in time) that let do that.

You will when ES7 happens (with await).

You can if you use another framework or a transpiler (as suggested in the article linked - Traceur transpiler or Spawn).

You can if you roll your own implementation!

like image 1
jhsowter Avatar answered Oct 18 '22 00:10

jhsowter