web-dev-qa-db-fra.com

Retards entre les promesses dans la chaîne de promesses

Supposons que j'utilise le code suivant pour exécuter plusieurs promesses en série:

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

Le code appelle simplement mySpecialFunction (qui renvoie une promesse), attend que la promesse soit résolue puis appelle à nouveau mySpecialFunction, etc. Ainsi, la fonction est appelée une fois pour chaque élément du tableau, dans le bon ordre.

Comment puis-je m'assurer qu'il y a un délai d'au moins 50 millisecondes entre chaque appel de mySpecialFunction(item)

Il est important que les promesses soient exécutées dans le bon ordre et que le temps d'exécution de mySpecialFunction varie à chaque fois. 

J'imagine qu'un sommeil synchrone fonctionnerait, mais je ne prévois pas d'exécuter ce code dans un autre thread, ce qui provoquerait un blocage de l'interface utilisateur gênante dans le navigateur. 

Je ne sais pas si setTimer pourrait être utilisé pour cela. Je veux dire que je ne peux pas retarder le retour d'une promesse. 

9
Forivin

Les réponses sont bonnes, mais elles attendent trop longtemps puisque toutes les réponses attendent peu importe ou que l'opération réelle prenne déjà plus de 50 ms.

Vous pouvez utiliser Promise.all pour cela.

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
Benjamin Gruenbaum

Une fonction utilitaire très pratique à utiliser est quelque chose que j'appelle delay():

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

Ensuite, vous pouvez l’utiliser dans une chaîne de promesse comme celle-ci:

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());

Vous pouvez aussi créer une petite fonction d’utilité pour effectuer l’itération séquentielle avec un délai optionnel:

// 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());
}

Et vous l'utiliseriez comme ceci:

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

Pour obtenir un délai de au moins 50 ms, utilisez Promise.all:

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

Ici vous allez: 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 doit être le résultat de p.then (). Seulement de cette façon, vous enchaînez les promesses.

Remarquez, je l'ai changé en délai de 1000 ms juste pour mettre l'accent.

1
SagiSergeNadir

Vous trouverez ci-dessous un exemple de réalisation d'une promesse qui ne bloque pas mais attend pendant une période donnée:

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);
    });

Donc, selon ce que vous voulez, vous devriez pouvoir faire quelque chose comme ceci:

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

puisque cela semble être une exigence de mySpecialFunction, je le mettrais en œuvre ici. Pour que la fonction se retarde si elle est appelée moins de 50 ms après le dernier appel

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();
        })
    }
}

puis:

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())

donc peu importe où/comment mySpecialFunction est appelé, il y aura toujours un délai d'au moins 50 ms avant que le code ne soit exécuté à l'intérieur du rappel transmis.

0
Thomas