web-dev-qa-db-ger.com

Verzögerungen zwischen Versprechungen in der Versprechenskette

Angenommen, ich verwende den folgenden Code, um ein paar Versprechungen in Serie auszuführen:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return mySpecialFunction(item);
  })
}, Promise.resolve())

Der Code ruft einfach mySpecialFunction auf (die ein Versprechen zurückgibt), wartet auf die Auflösung des Versprechens und ruft dann mySpecialFunction erneut auf usw. Die Funktion wird also für jedes Element im Array in der richtigen Reihenfolge einmal aufgerufen.

Wie kann ich sicherstellen, dass zwischen jedem Aufruf von mySpecialFunction(item) eine Verzögerung von mindestens 50 Millisekunden liegt?

Es ist wichtig, dass die Versprechen in der richtigen Reihenfolge ausgeführt werden und die Ausführungszeit von mySpecialFunction jedes Mal variiert.

Ich denke, ein synchroner Schlaf würde funktionieren, aber ich plane nicht, diesen Code in einem separaten Thread auszuführen, sodass es zu ärgerlichen UI-Einfrierungen im Browser kommen würde.

Ich bin mir nicht sicher, ob setTimer dafür verwendet werden könnte. Ich meine, ich kann die Rückgabe eines Versprechens nicht verzögern.

9
Forivin

Die Antworten sind gut, aber sie warten zu lange, da alle Antworten unabhängig davon warten, ob der eigentliche Vorgang bereits mehr als 50 ms gedauert hat oder nicht.

Sie können Promise.all dafür verwenden.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return Promise.all([delay(50), mySpecialFunction(item)]);
  })
}, Promise.resolve())
15

Eine wirklich nützliche Utility-Funktion ist etwas, das ich delay() nenne:

function delay(t, val) {
    return new Promise(function(resolve) {
        if (t <= 0) {
            resolve(val);
        } else {
            setTimeout(resolve.bind(null, val), t);
        }
    });
}

Dann können Sie es in einer Versprechen-Kette wie folgt verwenden:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item, index) {
  return promise.then(function(result) {
    // no delay on first iteration
    var delayT = index ? 50 : 0;
    return delay(delayT, item).then(mySpecialFunction);
  })
}, Promise.resolve());

Sie können auch ein kleines Hilfsprogramm für die sequentielle Iteration mit optionaler Verzögerung erstellen:

// delayT is optional (defaults to 0)
function iterateSerialAsync(array, delayT, fn) {
    if (!fn) {
        fn = delayT;
        delayT = 0;
    }
    array.reduce(function(p, item, index) {
        return p.then(function() {
            // no delay on first iteration
            if (index === 0) delayT = 0;
            return delay(delayT, item).then(fn)
        });
    }, Promise.resolve());
}

Und dann würden Sie es so verwenden:

iterateSerialAsync(paramerterArr, 50, mySpecialFunction).then(function(finalVal) {
    // all done here
});
3
jfriend00

Verwenden Sie Promise.all, um eine Verzögerung von mindestens 50ms zu erhalten:

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t);
  });
}
parameterArr.reduce(function(promise, item) {
  return promise.then(function() {
    return Promise.all([
      mySpecialFunction(item),
      delay(50)
    ]);
  });
}, Promise.resolve());
2
Bergi

Hier gehts: https://jsbin.com/suvasox/edit?html,js,console

let paramerterArr = ['a','b','c','d','e','f']
paramerterArr.reduce((p, val) => {
  return p.then(() => {
    return new Promise((res) => {
      setTimeout(() => { res(mySpecialFunction(val)); }, 1000); 
    });
  });
}, Promise.resolve());

p muss das Ergebnis von p.then () sein. Nur so verketten Sie die Versprechen.

Beachten Sie, dass ich es nur zur Betonung auf 1000 ms Verzögerung geändert habe.

1
SagiSergeNadir

Das folgende Beispiel zeigt, wie Sie ein Versprechen erreichen können, das nicht blockiert, sondern auf einen bestimmten Zeitraum wartet:

function timedPromise(ms, payload) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(payload);
        }, ms);
    })
}


var time = Date.now();

timedPromise(1000)
    .then(function() {
        console.log(time - Date.now());
        return timedPromise(2000);
    }).then(function() {
        console.log(time - Date.now());
        return timedPromise(3000);
    });

Je nachdem, was Sie genau wollen, sollten Sie in der Lage sein, Folgendes zu tun:

let paramerterArr = ['a','b','c','d','e','f']
parameterArr.reduce(function(promise, item) {
  return promise.then(function(result) {
    return mySpecialFunction(item);
  }).then(function(specialResult) {
    return timedPromise(50, specialResult);
  });
}, Promise.resolve())
1
Davin Tryon

Hier ist meine Komplettlösung für verzögerte Versprechensfolgen:


function timeout_sequence_promise(promises = [], timeout = 200) {

    //fake promise used as buffer between promises from params
    const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

    //we need to create array of all promises with delayed buffers
    let delayed_promises = [];

    let total = promises.length;

    let current = 0;

    //every odd promise will be buffer
    while (current < total) {

      delayed_promises.Push(promises[current]);
      delayed_promises.Push(delay(timeout));

      current++;

    }

    return Promise.all(delayed_promises).then((result) => {

      //we need to filter results from empty odd promises
      return result.filter((item, index) => (index+2)%2 === 0);

    });


  }

Es wird eine Reihe von Versprechungen und eine Zeitüberschreitungsverzögerung in ms zwischen ihnen als Parameter empfangen.

Hoffe, es wird dir helfen!

0
saike

da dies eine Anforderung von mySpecialFunction zu sein scheint, würde ich es dort implementieren. Damit verzögert sich die Funktion, wenn sie weniger als 50ms nach dem letzten Aufruf aufgerufen wird

const delayBetweenCalls = (delay, fn) => {
    let lastCall = NaN;
    return function(/*...arguments*/){
        //this and arguments are both forwarded to fn

        return new Promise(resolve => {
            let poll = () => {
                let delta = Date.now() - lastCall;
                if(delta < delay){
                    setTimeout(poll, delta - delay);
                }else{
                    lastCall = Date.now();
                    resolve( fn.apply(this, arguments) );
                }
            }
            poll();
        })
    }
}

dann:

const mySpecialFunction = delayBetweenCalls(50, function(some, ...args){
    return someValueOrPromise;
});

//and your loop stays the same:
parameterArr.reduce(function(promise, item) {
    return promise.then(function(result) {
        return mySpecialFunction(item);
    })
}, Promise.resolve())

damit es keine Rolle spielt, wo/wie mySpecialFunction aufgerufen wird, wird es immer eine Verzögerung von mindestens 50 ms geben, bevor der Code innerhalb des übergebenen Rückrufs ausgeführt wird.

0
Thomas