web-dev-qa-db-ja.com

プロミスチェーンのプロミス間の遅延

次のコードを使用して、2つのPromiseを連続して実行するとします。

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

コードは単にmySpecialFunction(promiseを返す)を呼び出し、promiseが解決されるのを待ってから、再びmySpecialFunctionを呼び出します。したがって、関数は配列内のすべての要素に対して正しい順序で1回呼び出されます。

mySpecialFunction(item)の呼び出しごとに少なくとも50ミリ秒の遅延があることを確認するにはどうすればよいですか?

Promiseが正しい順序で実行され、mySpecialFunctionの実行時間が毎回異なることが重要です。

同期スリープは機能すると思いますが、このコードを別のスレッドで実行する予定はないので、ブラウザで迷惑なuiフリーズが発生します。

SetTimerが何らかの方法でこれに使用できるかどうかはわかりません。約束の返却を遅らせることはできません。

12
Forivin

回答は良いですが、すべての回答が実際の操作に既に50ミリ秒以上かかったかどうかのに関係なく待機するため、待機時間が長すぎます。

Promise.allを使用できます。

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

便利なユーティリティ関数は、delay()と呼んでいます。

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

その後、次のようなプロミスチェーンで使用できます。

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

オプションの遅延を使用して順次反復を実行するための小さなユーティリティ関数を作成することもできます。

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

そして、あなたはそれをこのように使うでしょう:

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

少なくとも 50msの遅延を取得するには、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

ここに行きます: 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はp.then()の結果でなければなりません。その方法でのみ、約束を連鎖させます。

強調のために、1000msの遅延に変更しました。

1
SagiSergeNadir

以下は、ブロックせずに指定された期間待機するプロミスを実現する方法の例を示しています。

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

したがって、正確に何をしたいかに応じて、次のようなことができるはずです。

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

遅延されたプロミスシーケンスの完全なソリューションは次のとおりです。


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

    });


  }

パラメータとして、約束の配列とそれらの間のミリ秒単位のタイムアウト遅延を受け取ります。

よろしくお願いいたします。

0
saike

これはmySpecialFunctionの要件であると思われるので、ここで実装します。つまり、最後の呼び出しから50ミリ秒以内に呼び出された場合、関数はそれ自体を遅延させます。

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

次に:

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

したがって、mySpecialFunctionがどこでどのように呼び出されたかは関係ありません。渡されたコールバック内でコードを実行する前に、常に少なくとも50msの遅延があります。

0
Thomas