How do you implement a “raceToSuccess” helper, given a list of promises?

后端 未结 7 1815
孤城傲影
孤城傲影 2020-12-02 23:37

I\'m puzzled by something in the ES6 Promise API. I can see a clear use case for submitting multiple async jobs concurrently, and \"resolving\" on the first success. This wo

7条回答
  •  日久生厌
    2020-12-02 23:41

    I expanded the @loganfsmyth approach with timeouts and I wrote a small function that:

    • runs all the promises,
    • wait for the promises to succeed for no more than a specified amount of time (options.timeOutMs),
    • return the first that succeed.

    In the following snippet, you can test it:

    const firstThatCompleteSuccessfullyES6 = (options) => {
    
        // return the first promise that resolve
        const oneSuccess = (promises) => Promise.all(promises.map(p => {
                        // If a request fails, count that as a resolution so it will keep
                        // waiting for other possible successes. If a request succeeds,
                        // treat it as a rejection so Promise.all immediately bails out.
                        return p.then(
                            (val) => { return Promise.reject(val); },
                            (err) => { return Promise.resolve(err); }
                        );
                })
                ).then(
                    // If '.all' resolved, we've just got an array of errors.
                    (errors) => { return Promise.reject(errors); },
    
                    // If '.all' rejected, we've got the result we wanted.
                    (val) => { return Promise.resolve(val); }
                );
        
    
        // return the promise or reect it if timeout occur first
        const timeoutPromise = (ms, promise) => new Promise(function(resolve, reject) {
                setTimeout(() => reject(new Error('timeout')), ms);
                promise.then(resolve, reject);
            });
        
    
        if (options.subsystems.length < 1) {
            return Promise.reject('Parameters error, no subSystems specified');
        }
    
        const timedOutSubsystems = options.subsystems.map(function(subsystem){
            return timeoutPromise(options.timeOutMs, subsystem(options));
        });
    
        const startDate = Date.now();
    
        return oneSuccess(
            timedOutSubsystems
        )
        .then((result) => {
            const elapsedTime = Math.abs((startDate - Date.now()) / 1000);
            console.log('firstThatCompleteSuccessfully() done, after s: ' + elapsedTime + ': '+ result);
            return result;
        })
        .catch((error) => {
            const elapsedTime = Math.abs((startDate - Date.now()) / 1000);
            console.error('firstThatCompleteSuccessfully() error/nodata: ' + error);
        });
    
    }
    
    
    
    // example of use with two promises (subsystem1 & subsystem2) that resolves after a fixed amount of time
    
    const subsystem1 = (options) => new Promise(function(resolve, reject) {
            setTimeout(function(){
                console.log('subsystem1 finished');
                resolve('subsystem 1 OK');
            }, 1000);
        });
    
    
    
    const subsystem2 = (options) => new Promise(function(resolve, reject) {
            setTimeout(function(){
                console.log('subsystem2 finished');
                resolve('subsystem 2 OK');
            }, 2000);
        });
    
    
    firstThatCompleteSuccessfullyES6({
        subsystems: [subsystem1, subsystem2],
        timeOutMs: 2000
    })
    .then((result) => console.log("Finished: "+result));

提交回复
热议问题