Replacing $http with Fetch API

前端 未结 2 2020
梦如初夏
梦如初夏 2021-01-19 02:45

I\'m replacing $http with Fetch API and got replaced $q with Promise API. Because of that, Angular didn\'t run digest cycles anymore, thus UI didn\

2条回答
  •  既然无缘
    2021-01-19 03:30

    Rationale

    Wrapping $q.when will work but in my team's experience it will be very finicky and prone to error. As one example, returning $q.when from inside the body of a Promise.then function will still chain as a regular Promise and you won't get a $digest on callbacks.

    It also requires all authors to understand the difference between two very similar looking constructs (Promise/$q) and care about their concrete types for every level of an asynchronous call. If you are using modern conveniences like async/await (which abstracts the Promise types further), you're gonna be in even more trouble. Suddenly none of your code can be framework agnostic.

    Our team decided it was worth committing a big monkey patch to ensure all the promises (and the async/await keywords) "just worked" without needing additional thinking.

    Ugly? Yes. But we felt it was an okay tradeoff.


    Patch Promise callbacks to always apply $rootScope

    First we install the patch against Promise in a angular.run block:

    angular.module(...).run(normalizePromiseSideEffects);
    
    normalizePromiseSideEffects.$inject = ['$rootScope'];
    
    function normalizePromiseSideEffects($rootScope) {
      attachScopeApplicationToPromiseMethod('then');
      attachScopeApplicationToPromiseMethod('catch');
      attachScopeApplicationToPromiseMethod('finally');
    
      function attachScopeApplicationToPromiseMethod(methodName) {
        const NativePromiseAPI = window.Promise;
        const nativeImplementation = NativePromiseAPI.prototype[methodName];
    
        NativePromiseAPI.prototype[methodName] = function(...promiseArgs) {
          const newPromiseArgs = promiseArgs.map(wrapFunctionInScopeApplication);
          return nativeImplementation.bind(this)(...newPromiseArgs);
        };
      }
    
      function wrapFunctionInScopeApplication(fn) {
        if (!isFunction(fn) || fn.isScopeApplicationWrapped) {
          return fn;
        }
    
        const wrappedFn = (...args) => {
          const result = fn(...args);
          // this API is used since it's $q was using in AngularJS src
          $rootScope.$evalAsync();
          return result;
        };
        wrappedFn.isScopeApplicationWrapped = true;
        return wrappedFn;
      }
    }
    

    Async/Await

    If you want to support the use of async/await, you'll also need to configure Babel to always implement the syntax as Promises. We used babel-plugin-transform-async-to-promises.

提交回复
热议问题