Typescript async/await doesnt update AngularJS view

前端 未结 10 502
小鲜肉
小鲜肉 2020-12-01 09:17

I\'m using Typescript 2.1(developer version) to transpile async/await to ES5.

I\'ve noticed that after I change any property which is bound to view in my async funct

10条回答
  •  自闭症患者
    2020-12-01 09:43

    I've set up a fiddle showcasing the desired behavior. It can be seen here: Promises with AngularJS. Please note that it's using a bunch of Promises which resolve after 1000ms, an async function, and a Promise.race and it still only requires 4 digest cycles (open the console).

    I'll reiterate what the desired behavior was:

    • to allow the usage of async functions just like in native JavaScript; this means no other 3rd party libraries, like $async
    • to automatically trigger the minimum number of digest cycles

    How was this achieved?

    In ES6 we've received an awesome featured called Proxy. This object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

    This means that we can wrap the Promise into a Proxy which, when the promise gets resolved or rejected, triggers a digest cycle, only if needed. Since we need a way to trigger the digest cycle, this change is added at AngularJS run time.

    function($rootScope) {
      function triggerDigestIfNeeded() {
        // $applyAsync acts as a debounced funciton which is exactly what we need in this case
        // in order to get the minimum number of digest cycles fired.
        $rootScope.$applyAsync();
      };
    
      // This principle can be used with other native JS "features" when we want to integrate 
      // then with AngularJS; for example, fetch.
      Promise = new Proxy(Promise, {
        // We are interested only in the constructor function
        construct(target, argumentsList) {
          return (() => {
            const promise = new target(...argumentsList);
    
            // The first thing a promise does when it gets resolved or rejected, 
            // is to trigger a digest cycle if needed
            promise.then((value) => {
              triggerDigestIfNeeded();
    
              return value;
            }, (reason) => {
              triggerDigestIfNeeded();
    
              return reason;
            });
    
            return promise;
          })();
        }
      });
    }
    

    Since async functions rely on Promises to work, the desired behavior was achieved with just a few lines of code. As an additional feature, one can use native Promises into AngularJS!

    Later edit: It's not necessary to use Proxy as this behavior can be replicated with plain JS. Here it is:

    Promise = ((Promise) => {
      const NewPromise = function(fn) {
        const promise = new Promise(fn);
    
        promise.then((value) => {
          triggerDigestIfNeeded();
    
          return value;
        }, (reason) => {
          triggerDigestIfNeeded();
    
          return reason;
        });
    
        return promise;
      };
    
      // Clone the prototype
      NewPromise.prototype = Promise.prototype;
    
      // Clone all writable instance properties
      for (const propertyName of Object.getOwnPropertyNames(Promise)) {
        const propertyDescription = Object.getOwnPropertyDescriptor(Promise, propertyName);
    
        if (propertyDescription.writable) {
          NewPromise[propertyName] = Promise[propertyName];
        }
      }
    
      return NewPromise;
    })(Promise) as any;

提交回复
热议问题