casenonsensitive casenonsensitive - 2 months ago 22
AngularJS Question

Defer / postpone promise resolve

I want to resolve a promise object the same way it would be done Angular's defer.resolve with the Javascript Promise object.
Put another way: I want to create a dummy promise object to be returned. This promise will be resolved later on.

In Angular I would write it this way:

...
$scope.checkThis = { test: false };

function getPromise() {
var deferred = $q.defer();
var data = [1, 2, 3];

function resolvePromise() {
deferred.resolve(data);
}

$scope.$watch("checkThis.test", function(newVal, oldVal) {
if (newVal) {
resolvePromise();
}
});

return deferred.promise;
}

$scope.getData1 = function() {
return getPromise();
};

$scope.getData2 = function() {
return getPromise();
};

...


How would I achieve the same with the plain Javascript Promise object? I don't see how to use the Promise constructor as there is one event ($scope.checkThis.test becoming true) that will trigger several resolve.

Answer

Standard promises use the revealing constructor pattern. There is a straightforward and preferred way to do it:

function getPromise() {
  return new Promise(resolve => {
    var data = [1, 2, 3];
    $scope.$watch("checkThis.test", function(newVal, oldVal) {
      if (newVal) {
        resolve(data);
      }
    });
  });
}

Watch out! Standard promises will not trigger an angular digest loop, changes made to scopes inside promise callbacks may not be reflected on the UI and watchers immediately. This is the reason you cannot just change $q promises to standard Promises.

If for some reason you cannot apply the revealing constructor pattern to your scenario, here is a way to create a similar deferred class. Remember, you can always avoid using this class, I am just leaving it here so visitors see how to change from older promise libraries without the need to restructure their existing code. No new code should use this:

class Deferred {
  constructor() {
    this.resolve = null;
    this.reject = null;
    this.promise = null;

    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }
}