Fire async request in parallel but get result in order using rxjs

后端 未结 2 597
灰色年华
灰色年华 2021-01-13 03:09

For example:

Get 5 pages in parrallel using jquery ajax. When page2 return, do nothing. When page1 return, do something with page1 and page2.

// assu         


        
2条回答
  •  南笙
    南笙 (楼主)
    2021-01-13 03:55

    concatMap keeps the order, but processes elements in sequence.

    mergeMap does not keep the order, but it runs in parallel.

    I've created the operator mergeMapAsync below to make process elements (e.g. page downloads) in parallel but emit in order. It even supports throttling (e.g. download max. 6 pages in parallel).

    Rx.Observable.prototype.mergeMapAsync = mergeMapAsync;
    function mergeMapAsync(func, concurrent) {
        return new Rx.Observable(observer => {
            let outputIndex = 0;
            const inputLen = this.array ? this.array.length : this.source.array.length;
            const responses = new Array(inputLen);
    
            const merged = this.map((value, index) => ({ value, index })) // Add index to input value.
                .mergeMap(value => {
                    return Rx.Observable.fromPromise(new Promise(resolve => {
                        console.log(`${now()}: Call func for ${value.value}`);  
                        // Return func retVal and index.
                        func(value.value).then(retVal => {
                            resolve({ value: retVal, index: value.index });
                        });
                    }));
                }, concurrent);
    
            const mergeObserver = {
                next: (x) => {
                    console.log(`${now()}: Promise returned for ${x.value}`);
                    responses[x.index] = x.value;
    
                    // Emit in order using outputIndex.
                    for (let i = outputIndex, len = responses.length; i < len; i++) {
                        if (typeof responses[i] !== "undefined") {
                            observer.next(responses[i]);
                            outputIndex = i + 1;
                        } else {
                            break;
                        }
                    }
                },
                error: (err) => observer.error(err),
                complete: () => observer.complete()
            };
            return merged.subscribe(mergeObserver);
        });
    };
    
    // ----------------------------------------
    const CONCURRENT = 3;
    var start = Date.now();
    var now = () => Date.now() - start;
    
    const array = ["a", "b", "c", "d", "e"];
    Rx.Observable.from(array)
        .mergeMapAsync(value => getData(value), CONCURRENT)
        .finally(() => console.log(`${now()}: End`))
        .subscribe(value => {
            console.log(`${now()}: ${value}`); // getData
        });
    
    function getData(input) {
        const delayMin = 500; // ms
        const delayMax = 2000; // ms
    
        return new Promise(resolve => {
            setTimeout(() => resolve(`${input}+`), Math.floor(Math.random() * delayMax) + delayMin);
        });
    }
    
    
    
      
      
      mergeMapAsync
      
    
    
    
    
    

提交回复
热议问题