2016-02-05 17 views
5

私はjavascriptで約束しているデバウンス機能を実装しようとしています。こうすることで、各呼び出し側はPromiseを使用して「デバウンス」機能の結果を消費することができます。私が持つEventEmitterへの依存性を導入(または持つEventEmitterの自分自身の基本的なバージョンを実装する)せずにこのユーティリティ関数を実装したいと思い、理想的には約束で実装されたデバウンス機能

function debounce(inner, ms = 0) { 
    let timer = null; 
    let promise = null; 
    const events = new EventEmitter(); // do I really need this? 

    return function (...args) { 
    if (timer == null) { 
     promise = new Promise(resolve => { 
     events.once('done', resolve); 
     }); 
    } else { 
     clearTimeout(timer); 
    } 

    timer = setTimeout(() => { 
     events.emit('done', inner(...args)); 
     timer = null; 
    }, ms); 

    return promise; 
    }; 
} 

:ここで私はこれまで思い付くことができた最高ですしかし、私はそれをする方法を考えることができません。何かご意見は?

+0

どのように 'debounce'機能が使用されようとしていますか? – Aprillion

+0

「内部」とは何ですか? – thefourtheye

+0

@Aprillionこれは、[lodash](https://lodash.com/docs#debounce)で提供されているデバウンス機能のように、標準のデバウンス機能として使用されます。ただし、呼び出し元が内部の戻り値にアクセスできるようにしたい機能を約束する。それはあなたの質問に答えますか?もしそうでなければ、私はさらに精緻化することができます。 – Chris

答えて

12

私は約束でこれを実装するためのより良い方法を見つけました:私はまだ歓迎の提案

function debounce(inner, ms = 0) { 
    let timer = null; 
    let resolves = []; 

    return function (...args) {  
    // Run the function after a certain amount of time 
    clearTimeout(timer); 
    timer = setTimeout(() => { 
     // Get the result of the inner function, then apply it to the resolve function of 
     // each promise that has been created since the last time the inner function was run 
     let result = inner(...args); 
     resolves.forEach(r => r(result)); 
     resolves = []; 
    }, ms); 

    return new Promise(r => resolves.push(r)); 
    }; 
} 

が、新しい実装が持つEventEmitterへの依存(またはのような何かせずにこの機能を実装する方法についての私の元の質問に答えますそれ)。

1

あなたのニーズに大きく依存しているため、達成しようとしていることが分かりません。以下はやや一般的なものです。下のコードで何が起こっているのかをしっかりと把握していなければ、実際には使用したくないかもしれません。

ビューコンソール

// Debounce state constructor 
 
function debounce(f) { 
 
    this._f = f; 
 
    return this.run.bind(this) 
 
} 
 

 
// Debounce execution function 
 
debounce.prototype.run = function() { 
 
    console.log('before check'); 
 
    if (this._promise) 
 
    return this._promise; 
 
    console.log('after check'); 
 
    return this._promise = this._f(arguments).then(function(r) { 
 
    console.log('clearing'); 
 
    delete this._promise; // remove deletion to prevent new execution (or remove after timeout?) 
 
    return r; 
 
    }.bind(this)).catch(function(r) { 
 
    console.log('clearing after rejection'); 
 
    delete this._promise; // Remove deletion here for as needed as noted above 
 
    return Promise.reject(r); // rethrow rejection 
 
    }) 
 
} 
 

 
// Some function which returns a promise needing debouncing 
 
function test(str) { 
 
    return new Promise(function(resolve, reject) { 
 
    setTimeout(function() { 
 
     console.log('test' + str); 
 
     resolve(); 
 
    }, 1000); 
 
    }); 
 
} 
 

 
a = new debounce(test); // Create debounced version of function 
 
console.log("p1: ", p1 = a(1)); 
 
console.log("p2: ", p2 = a(2)); 
 
console.log("p1 = p2", p1 === p2); 
 
setTimeout(function() { 
 
    console.log("p3: ", p3 = a(3)); 
 
    console.log("p1 = p3 ", p1 === p3, " - p2 = p3 ", p2 === p3); 
 
}, 2100)
上記のコードを実行しています。私は、何が起こっているのかを少し見せてくれるいくつかのメッセージを書いています。まず、約束を返す関数が、 new debounce()の引数として渡されます。これにより、デバッグされたバージョンの関数が作成されます。

上記のコードのようにデバウンス関数を実行すると(a(1), a(2), and a(3))、処理中に新しいものを開始するのではなく、同じ約束を返すことがわかります。約束が完了したら、それは古い約束を取り除きます。上記のコードでは、(3)を実行する前にsetTimeoutを使ってタイムアウトを手動で待っています。

debounce.prototypeでリセットまたはクリア機能を追加するなど、別の方法でも約束をクリアすることができます。タイムアウトに設定することもできます。コンソールログのテストではp1が表示され、p2は同じ約束を得ます(参照比較「===」は真です)。そして、p3は異なっています。

0

私は約束の戻り値を得たいと思っていたのでここに上陸しましたが、アンダースコアでデバウンスを行いました.jsは代わりにundefinedを返していました。私はlodashバージョンのleading = trueを使ってしまいました。実行が先行しているのか後続しているのかは気にしないので、私の場合はうまくいきます。

https://lodash.com/docs/4.17.4#debounce

_.debounce(somethingThatReturnsAPromise, 300, { 
    'leading': true, 
    'trailing': false 
}) 
関連する問題