0

With callbacks, I wrap the callback in an Angular $timeout block so that it updates the UI.

function makeServerRequest(callback) {
  $timeout(callback);
}

makeServerRequest((response) => {
  $scope.updateUI();
})

This operates within the Angular digest cycle, and the UI gets updated.

However, this doesn't work:

function makeServerRequest() {
  return new Promise((resolve, reject) => {
    $timeout(() => {
      resolve();
    });
  })
}

makeServerRequest().then(() => {
  $scope.updateUI();
})

In this case, it seems like the resolve happens, and then $scope.updateUI(); is called after the timeout.

How can I wrap resolve inside a timeout, so that the outside .then is called inside a timeout?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Snowman
  • 31,411
  • 46
  • 180
  • 303
  • 1
    It is a good idea to use `$q` instead of native promises which has handlers that will be evaluated during a digest cycle. – BShaps Jul 03 '18 at 00:19
  • 1
    Best practice is to avoid ES6 promises. Or if coming from a 3rd party library, convert them to AngularJS promises with [$q.when](https://docs.angularjs.org/api/ng/service/$q#when). – georgeawg Jul 03 '18 at 00:29
  • Possible duplicate of [How to create a AngularJS promise from a callback-based API](https://stackoverflow.com/a/43134613/5535245). – georgeawg Jul 03 '18 at 01:03
  • @georgeawg does the sole reason for this best practice have to do with digest cycles? – Snowman Jul 03 '18 at 14:26
  • AngularJS modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and AngularJS execution context. Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc. AngularJS $q promises are integrated with the framework execution context. – georgeawg Jul 03 '18 at 15:32

2 Answers2

1

Native Promise doesn't trigger a digest, it should be triggered manually in this case:

makeServerRequest().then(() => {
  $scope.updateUI();
  $scope.$apply()
})

And $timeout already returns a promise ($q promise, which is digest-friendly). So there's no need to create a new one, especially a native one.

It can be:

function makeServerRequest() {
  return $timeout(angular.noop, 100);
}

makeServerRequest().then(...);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • So the key then is just to return a $timeout object? – Snowman Jul 02 '18 at 22:30
  • Yes. setTimeout needs to be promisified to become a promise. $timeout doesn't need that. It's unclear why you use a timeout in makeServerRequest, but I assume you emulate it. – Estus Flask Jul 03 '18 at 08:17
0

What you need to do is create a $q promise from the server request API:

function makeServerRequest() {
  return $q((resolve, reject) => {
     var callback = resolve;
     ServerRequest(callback);
  });  
}

makeServerRequest().then(() => {
  $scope.updateUI();
})

There is no need to use $timeout as the $q Service is integrated with the AngularJS framework and its digest cycle.

For more information, see AngularJS $q Service API Reference - $q constructor.

georgeawg
  • 48,608
  • 13
  • 72
  • 95