angularjs deferred promise not deferring

2019-07-19 16:59发布

问题:

struggling to get promises working correctly in angularjs service provider i've read the docs as well as numerous examples (here, here and here) and i think i've got my syntax ok (although obviously something is wrong)

app module and controller look like

var myApp = angular.module('myApp', []);

myApp.controller('Controller_1', ['$scope', 'Service_1', function($scope, Service_1) {

    var myName = "Ben";

    Service_1.slowService(myName)
        .then(Service_1.fastService(name));

    $scope.myName = myName;
}]);

the service (with the slow function) looks like this:

myApp.service('Service_1', function($q) {
    this.slowService = function (name) {
        var deferred = $q.defer();
        console.log('Start of slowService:', name, Date.now());

        setTimeout(function() {
            console.log('setTimeout name:', name, Date.now());

            if(name){
                name = 'Hello, ' + name + " is learning Angularjs";
                alert(name); 
                    console.log('name:', name);
                deferred.resolve(name);
            } else {
                deferred.reject('No name supplied !');
            }
        }, 3000);

        return deferred.promise;
    };

    this.fastService = function(name){ 
        console.log('Start of fastFunction:', name, Date.now());
        alert('Hello ' + name + ' - you are quick!'); 
    }; 
});

console output looks like this:

Start of slowService: Ben 1420832940118
Start of fastFunction: result 1420832940122
setTimeout name: Ben 1420832948422
name: Hello, Ben is learning Angularjs

The fastService is starting before the slowService completes, despite using a deferred object / promise in Service_1 and a .then in the controller...

Can anyone point out what's wrong with the code ?

jsfiddle is here

EDIT: put the fast function in the service so there's no confusion with hoisting etc - still the same result - js fiddle updated

回答1:

The fastService is starting before the slowService completes

That is because you are executing the function fastService before slowService async callback runs.Instead you would want to provide the reference of the function. i.e .then(Service_1.fastService(name)); should be .then(Service_1.fastService); or .then(function(name){ Service_1.fastService(name); }); otherwise fastservice will just run right away, before the async part of the slowService runs.

Use $timeout with that the advantage is that it already returns a promise so you do not need to create a deferred object and resulting in deferred anti-pattern.

myApp.service('Service_1', function($q, $timeout) { //<-- Inject timeout
    this.slowService = function (name) {
        console.log('Start of slowService:', name, Date.now());

        return $timeout(function() {
            console.log('setTimeout name:', name, Date.now());

            if(name){
                name = 'Hello, ' + name + " is learning Angularjs";
                return name; //return data
           }
           //Reject the promise
           return $q.reject('No name supplied !');

        }, 3000);


    };
   //...
});

and when consuming just chain through:

  Service_1.slowService(myName)
     .then(Service_1.fastService);

So instead of timeout even if you are using $http in your original method just return the promise from http rather than creating a deferred object. Also just keep in mind when you use the syntax .then(Service_1.fastService); and if you are referring to this context in the fast service it wont be the service instance.



回答2:

The way you are passing your function to then() part is not correct. Pass it like this :

Service_1.slowService(myName)
         .then(function(){fastFunction(name)});


回答3:

Since your slowService resolves with a name value, that is what is going to be provided as the parameter of the function you pass into then. You could do this:

Service_1.slowService(myName)
.then(function(name){ Service_1.fastFunction(name); });

but even this is redundant since you are just handing off the name to another function that takes a single name parameter.

This is all you need:

Service_1.slowService(myName)
.then(Service_1.fastFunction);

Notice that there is no (name) in that second line. We want to pass the function into then. What you were doing was calling the function immediately and passing undefined into then.



回答4:

YAAK is correct. You could also write it like this:

Service_1.slowService(myName) .then(fastService);

Just to clarify a bit on what I'm pretty sure is going on:

The then function is registering callbacks for when the promise is either resolved or rejected. You don't need to pass the function parameters, because that is already done when you resolve with whatever data you are trying to pass to the callback function. When you had fastService(name) in there, it was just executing the function as soon as it hit that line of code without waiting for the promise to resolve since it was a function call, and not a function object.

It's interesting that fastService returned 'result' as name instead of saying that name is undefined. I don't know where it is getting a value for that variable from.