Add queueing to angulars $http service

前端 未结 6 1466
迷失自我
迷失自我 2020-12-07 17:24

I have a very quirky api that can only handle a single request at a time. Therefore, I need to ensure that every time a request is made, it goes into a queue, and that queue

相关标签:
6条回答
  • 2020-12-07 17:33

    Building on Valentyn's great work above, I rolled this code into a standalone Angular (v1.2+) request/response interceptor. It will queue $http requests automatically without needing to rework your code to use srv() everywhere:

    ( function() {
    
    'use strict';
    
    angular.module( 'app' ).config( [ '$httpProvider', function( $httpProvider ) {
    
        /**
         * Interceptor to queue HTTP requests.
         */
    
        $httpProvider.interceptors.push( [ '$q', function( $q ) {
    
            var _queue = [];
    
            /**
             * Shifts and executes the top function on the queue (if any). Note this function executes asynchronously (with a timeout of 1). This
             * gives 'response' and 'responseError' chance to return their values and have them processed by their calling 'success' or 'error'
             * methods. This is important if 'success' involves updating some timestamp on some object which the next message in the queue relies
             * upon.
             */
    
            function _shiftAndExecuteTop() {
    
                setTimeout( function() {
    
                    _queue.shift();
    
                    if ( _queue.length > 0 ) {
                        _queue[0]();
                    }
                }, 1 );
            }
    
            return {
    
                /**
                 * Blocks each request on the queue. If the first request, processes immediately.
                 */
    
                request: function( config ) {
    
                    var deferred = $q.defer();
                    _queue.push( function() {
    
                        deferred.resolve( config );
                    } );
    
                    if ( _queue.length === 1 ) {
                        _queue[0]();
                    }
    
                    return deferred.promise;
                },
    
                /**
                 * After each response completes, unblocks the next request.
                 */
    
                response: function( response ) {
    
                    _shiftAndExecuteTop();
                    return response;
                },
    
                /**
                 * After each response errors, unblocks the next request.
                 */
    
                responseError: function( responseError ) {
    
                    _shiftAndExecuteTop();
                    return $q.reject( responseError );
                },
            };
        } ] );
    } ] );
    
    } )();
    
    0 讨论(0)
  • 2020-12-07 17:36

    My two queues:

    1. Sequential queue QueueHttp: executes requests one after the other

    code:

    app.factory('QueueHttp', ($http, $q) => {
        let promise = $q.resolve();
    
        return (conf) => {
            let next = () => {
                return $http(conf);
            };
    
            return promise = promise.then(next);
        };
    });
    

    usage:

    return QueueHttp({
               url: url,
               method: 'GET'
           });
    
    1. Delay queue DelayHttp: executes requests by a delay amount

    code:

    app.factory('DelayHttp', ($http, $timeout) => {
        let counter = 0,
            delay = 100;
    
        return (conf) => {
            counter += 1;
    
            return $timeout(() => {
                counter -= 1;
                return $http(conf);
            }, counter * delay);
        };
    });
    

    usage:

    return DelayHttp({
               url: url,
               method: 'GET'
           });
    
    0 讨论(0)
  • 2020-12-07 17:40
    .factory('qHttp', function($q, $http) {
      var queue = $q.when();
    
      return function queuedHttp(httpConf) {
        var f = function(data) {
          return $http(httpConf);
        };
        return queue = queue.then(f, f);
      };
    })
    

    How to use:

    var apis = ['//httpbin.org/ip', '//httpbin.org/user-agent', '//httpbin.org/headers'];
    
    for (var i = 0; i < 100; i++) {
      qHttp({
        method: 'get', 
        url: apis[i % apis.length]
      })
      .then(function(data) { 
        console.log(data.data); 
      });
    }
    
    0 讨论(0)
  • 2020-12-07 17:44

    Here is my solution for that: http://plnkr.co/edit/Tmjw0MCfSbBSgWRhFvcg

    The idea is: each run of service add request to queue and return promise. When request to $http is finished resolve/refuse returned promise and execute next task from queue if any.

    app.factory('srv', function($q,$http) {
    
      var queue=[];
      var execNext = function() {
        var task = queue[0];
        $http(task.c).then(function(data) {
          queue.shift();
          task.d.resolve(data);
          if (queue.length>0) execNext();
        }, function(err) {
          queue.shift();
          task.d.reject(err);
          if (queue.length>0) execNext();
        })
        ;
      }; 
      return function(config) {
        var d = $q.defer();
        queue.push({c:config,d:d});
        if (queue.length===1) execNext();            
        return d.promise;
      };
    });
    

    Looks quite simple :)

    0 讨论(0)
  • 2020-12-07 17:48

    If someone need solution to sequential http calls (as mentioned by OP) in Angular 5 then following is the solution:

        import { Injectable } from '@angular/core';
        import { Response } from '@angular/http';
        import { HttpClient } from '@angular/common/http';
        import { Observable } from 'rxjs/Observable';
        import { Subject } from 'rxjs/Subject'
    
        export class PendingRequest {
          url: string;
          method: string;
          options: any;
          subscription: Subject<any>;
    
          constructor(url: string, method: string, options: any, subscription: Subject<any>) {
            this.url = url;
            this.method = method;
            this.options = options;
            this.subscription = subscription;
          }
        }
    
        @Injectable()
        export class BackendService {
          private requests$ = new Subject<any>();
          private queue: PendingRequest[] = [];
    
          constructor(private httpClient: HttpClient) {
            this.requests$.subscribe(request => this.execute(request));
          }
    
          /** Call this method to add your http request to queue */
          invoke(url, method, params, options) {
            return this.addRequestToQueue(url, method, params, options);
          }
    
          private execute(requestData) {
            //One can enhance below method to fire post/put as well. (somehow .finally is not working for me)
            const req = this.httpClient.get(requestData.url)
              .subscribe(res => {
                const sub = requestData.subscription;
                sub.next(res);
                this.queue.shift();
                this.startNextRequest();
              });
          }
    
          private addRequestToQueue(url, method, params, options) {
            const sub = new Subject<any>();
            const request = new PendingRequest(url, method, options, sub);
    
            this.queue.push(request);
            if (this.queue.length === 1) {
              this.startNextRequest();
            }
            return sub;
          }
    
          private startNextRequest() {
            // get next request, if any.
            if (this.queue.length > 0) {
              this.execute(this.queue[0]);
            }
          }
        }
    

    In case of someone wants to look at working plunker then here is the working plunker.

    0 讨论(0)
  • 2020-12-07 17:52

    Richard: Your code works perfect but it also works with inner request like template or $templateProviders.

    Here is solution to work only with external http requests

    /**
     * Interceptor to queue HTTP requests.
     */
    $httpProvider.interceptors.push(['$q', function ($q) {
        var _queue = [];
    
        /**
         * Executes the top function on the queue (if any).
         */
        function _executeTop() {
            if (_queue.length === 0) {
                return;
            }
            _queue[0]();
        }
    
        return {
            /**
             * Blocks each request on the queue. If the first request, processes immediately.
             */
            request: function (config) {
                if (config.url.substring(0, 4) == 'http') {
                    var deferred = $q.defer();
                    _queue.push(function () {
                        deferred.resolve(config);
                    });
                    if (_queue.length === 1) {
                        _executeTop();
                    }
                    return deferred.promise;
                } else {
                    return config;
                }
            },
            /**
             * After each response completes, unblocks the next request.
             */
            response: function (response) {
                if (response.config.url.substring(0, 4) == 'http') {
                    _queue.shift();
                    _executeTop();
                }
                return response;
            },
            /**
             * After each response errors, unblocks the next request.
             */
            responseError: function (responseError) {
                if (responseError.config.url.substring(0, 4) == 'http') {
                    _queue.shift();
                    _executeTop();
                }
                return $q.reject(responseError);
            },
        };
    }]);
    
    0 讨论(0)
提交回复
热议问题