From 6a3b83988901af625950ba2fc9cff6d7b1d967d7 Mon Sep 17 00:00:00 2001 From: Juha Paananen Date: Mon, 12 Feb 2018 22:32:42 +0200 Subject: [PATCH] Support options { leading, trailing } in throttle --- dist/Bacon.js | 53 ++++++++++++++++++++++++++++++---- dist/Bacon.min.js | 4 +-- dist/Bacon.noAssert.js | 55 ++++++++++++++++++++++++++++++----- package-lock.json | 2 +- spec/specs/throttle.coffee | 9 ++++++ src/throttle.js | 59 ++++++++++++++++++++++++++++++++++---- 6 files changed, 160 insertions(+), 22 deletions(-) diff --git a/dist/Bacon.js b/dist/Bacon.js index 01f4e7fca..d283e78f1 100644 --- a/dist/Bacon.js +++ b/dist/Bacon.js @@ -1434,7 +1434,7 @@ var Bacon = { CompositeUnsubscribe: CompositeUnsubscribe, never: never, constant: constant, - version: '2.0.5' + version: '' }; Bacon.Bacon = Bacon; @@ -3489,12 +3489,53 @@ Observable.prototype.takeWhile = function (f) { }); }; +var defaultOptions = { trailing: true }; + Observable.prototype.throttle = function (delay) { - return this.delayChanges(new Desc(this, "throttle", [delay]), function (changes) { - return changes.bufferWithTime(delay).map(function (values) { - return values[values.length - 1]; - }); - }); + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOptions; + var leading = options.leading, + trailing = options.trailing; + + var lastCallTime = 0; + var trailingValue, timeoutId, trailingEnd; + var cancelTrailing = function () { + if (timeoutId !== null) { + Bacon.scheduler.clearTimeout(timeoutId); + timeoutId = null; + } + }; + var stream = void 0; + var trailingCall = function () { + stream.dispatcher.push(trailingValue); + timeoutId = null; + trailingValue = null; + lastCallTime = Bacon.scheduler.now(); + if (trailingEnd) { + stream.dispatcher.push(trailingEnd); + } + }; + return stream = withDesc(new Desc(this, "throttle", options === defaultOptions ? [delay] : [delay, options]), this.withHandler(function (event) { + if (event.isNext) { + var curTime = Bacon.scheduler.now(); + if (lastCallTime === 0 && !leading) { + lastCallTime = curTime; + } + var remaining = delay - (curTime - lastCallTime); + if (remaining <= 0) { + cancelTrailing(); + lastCallTime = curTime; + return this.push(event); + } else if (trailing) { + trailingValue = event; + cancelTrailing(); + timeoutId = Bacon.scheduler.setTimeout(trailingCall, remaining); + } + } else if (event.isEnd && timeoutId !== null) { + trailingEnd = event; + } else { + return this.push(event); + } + })); }; Property.prototype.toEventStream = function (options) { diff --git a/dist/Bacon.min.js b/dist/Bacon.min.js index 1fad25d62..9bb5aee4e 100644 --- a/dist/Bacon.min.js +++ b/dist/Bacon.min.js @@ -1,2 +1,2 @@ -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):t.Bacon=n()}(this,function(){"use strict";function t(){}function n(t){return t}function r(t){return t.slice(0)}function e(t){for(var n=arguments.length,r=1;1n;12?e-2:0),u=2;u1?r-1:0),i=1;i1&&"."===t.charAt(0)}function M(t,n){return Nt.apply(void 0,[t].concat(n))}function T(t,n,r,e){if(n&&n._isProperty){var i=n.sampledBy(t,function(t,n){return[t,n]});return e.call(i,function(t){return t[0]}).map(function(t){return t[1]})}return n=M(n,r),e.call(t,n)}function O(t){if(Tt.isFunction(t))return t;if(I(t)){var n=W(t);return function(t,r){return t[n](r)}}throw new Error("not a function or a field key: "+t)}function W(t){return t.slice(1)}function P(t){this.desc=t,this.id=++Ft,this.initialDesc=this.desc}function V(t,n,r){E.call(this,n,r),this.property=t,this.subscribe=Tt.bind(this.subscribe,this),this.current=It,this.currentValueRootId=void 0,this.propertyEnded=!1}function H(t,n){return new D(t,N(It,n))}function D(t,n,r){P.call(this,t),this.dispatcher=new V(this,n,r),y(this)}function C(t,n,r,e){if(!(this instanceof C))return new C(t,n,r);Tt.isFunction(t)&&(r=n,n=t,t=Lt),e!==qt&&(n=B(this,n)),P.call(this,t),this.dispatcher=new E(n,r),y(this)}function B(t,n){var r=!1;return function(e){function i(){var t=o;o=null;for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];this.unsubscribe=Tt.bind(this.unsubscribe,this),this.unsubscribed=!1,this.subscriptions=[],this.starting=[];for(var n,r=0;r1?n-1:0),e=1;e0&&e.push(f),i+=2}return[r,e,u]}function Z(t,n){if(0===n.length)return q();var r=$(n),e=r[0],i=r[1],u=r[2];if(!e.length)return q();e=Tt.map(Q.fromObservable,e);var o=Tt.any(e,function(t){return t.flatten})&&X(Tt.map(function(t){return t.obs},e)),s=new b(jt,"when",u),c=t(s,function(t){function n(t){for(var n=0;n0))return Vt;for(var r,o=Vt,s=f.pop(),c=0;c1&&void 0!==arguments[1]?arguments[1]:[];return Tt.any(t,n)}function G(t){return Tt.isFunction(t)?t:Tt.always(t)}function J(t){return!t.sync||t.ended}function K(){for(var t=arguments.length,n=Array(t),r=0;r1&&void 0!==arguments[1]?arguments[1]:Tt.id;return new C(new b(jt,"fromBinder",[t,n]),function(r){var e=!1,i=!1,u=function(){if(!e)return void 0!==o&&null!==o?(o(),e=!0):i=!0},o=t(function(){for(var t,e=arguments.length,i=Array(e),o=0;o2&&void 0!==arguments[2]?arguments[2]:yt;return w(new b(jt,"fromPromise",[t]),ct(function(r){var e=t.then(r,function(t){return r(new h(t))});return e&&"function"==typeof e.done&&e.done(),n?function(){if("function"==typeof t.abort)return t.abort()}:function(){}},r))}function gt(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return w(new b(jt,"interval",[t,n]),vt(t,function(){return l(n)}))}function wt(){var t=rt(arguments);return t.length?new C(new b(jt,"mergeAll",t),function(n){var r=0,e=function(e){return function(i){return e.dispatcher.subscribe(function(e){if(e.isEnd)return r++,r===t.length?n(d()):Vt;var u=n(e);return u===Pt&&i(),u})}};return new F(Tt.map(e,t)).unsubscribe}):q()}function mt(t,n){var r=0;return w(new b(jt,"repeatedly",[t,n]),vt(t,function(){return n[r++%n.length]}))}function Et(t){var n=0;return ct(function(r){function e(t){return t.isEnd?u?i():u=!0:o=r(t)}function i(){var i;for(u=!0;u&&o!==Pt;)i=t(n++),u=!1,i?s=i.subscribeInternal(e):r(d());return u=!0}var u=!1,o=Vt,s=function(){};return i(),function(){return s()}})}function St(t,n){var r=0;return w(new b(jt,"sequentially",[t,n]),vt(t,function(){var t=n[r++];return r1?n-1:0),e=1;e0;)r[i]instanceof Function||(r[i]=Tt.always(r[i])),r[i]=function(t){return function(){for(var n=arguments.length,r=Array(n),e=0;e1&&void 0!==arguments[1]?arguments[1]:Tt.id,e=0;e1&&void 0!==arguments[1]?arguments[1]:Tt.id,e=0;e=0)return n.splice(r,1)},fold:function(t,n,r){for(var e,i=0;i5?"[..]":"["+Tt.map(Tt.toString,t).toString()+"]":null!=(null!=t?t.toString:void 0)&&t.toString!==Object.prototype.toString?t.toString():"object"==typeof t?Ot>5?"{..}":"{"+function(){var i=[];for(n in t)e.call(t,n)&&(r=function(){try{return t[n]}catch(t){return t}}(),i.push(Tt.toString(n)+":"+Tt.toString(r)));return i}()+"}":t}finally{Ot--}}},Ot=0,Wt=0;s.prototype._isEvent=!0,s.prototype.isEvent=!0,s.prototype.isEnd=!1,s.prototype.isInitial=!1,s.prototype.isNext=!1,s.prototype.isError=!1,s.prototype.hasValue=!1,s.prototype.filter=function(){return!0},s.prototype.inspect=function(){return this.toString()},s.prototype.log=function(){return this.toString()},s.prototype.toNext=function(){return this},i(c,s),c.prototype.isNext=!0,c.prototype.hasValue=!0,c.prototype.fmap=function(t){return this.apply(t(this.value))},c.prototype.apply=function(t){return new c(t)},c.prototype.filter=function(t){return t(this.value)},c.prototype.toString=function(){return Tt.toString(this.value)},c.prototype.log=function(){return this.value},c.prototype._isNext=!0,i(f,c),f.prototype._isInitial=!0,f.prototype.isInitial=!0,f.prototype.isNext=!1,f.prototype.apply=function(t){return new f(t)},f.prototype.toNext=function(){return new c(this.value)},i(a,s),a.prototype.isEnd=!0,a.prototype.fmap=function(){return this},a.prototype.apply=function(){return this},a.prototype.toString=function(){return""},i(h,s),h.prototype.isError=!0,h.prototype.fmap=function(){return this},h.prototype.apply=function(){return this},h.prototype.toString=function(){return" "+Tt.toString(this.error)};var Pt="",Vt="",Ht=[],Dt=function(t){return Ht.push(t)};e(b.prototype,{_isDesc:!0,deps:function(){return this.cached||(this.cached=m([this.context].concat(this.args))),this.cached},toString:function(){var t=Tt.map(Tt.toString,this.args);return Tt.toString(this.context)+"."+Tt.toString(this.method)+"("+t+")"}});var Ct={setTimeout:function(t,n){return setTimeout(t,n)},setInterval:function(t,n){return setInterval(t,n)},clearInterval:function(t){return clearInterval(t)},clearTimeout:function(t){return clearTimeout(t)},now:function(){return(new Date).getTime()}},Bt=function(){function t(){return Tt.toString({rootEvent:v,processingAfters:E,waiterObs:y,waiters:b,aftersStack:g,aftersStackHeight:w,flushed:m})}function n(t){t<=w||(g[t-1]||(g[t-1]=[[],0]),w=t)}function r(){return null!==v}function e(t,n){v?s(t,n):jt.scheduler.setTimeout(n,0)}function i(t,r){if(!v&&!E)return r();n(1);for(var e=0;e=t;){var e=g[w-1];if(!e)throw new Error("Unexpected stack top: "+e);var i=e[0],u=e[1];if(!(ut&&0==g[w-1][0].length;)w--}finally{c||(g=[],w=0)}}}finally{r&&(E=!1)}}}function s(t,n){if(v){var r=b[t.id];return void 0===r||null===r?(r=b[t.id]=[n],y.push(t)):r.push(n)}return n()}function c(){for(;y.length>0;)f(0,!0);m={}}function f(t,n){var r=y[t],e=r.id,i=b[e];y.splice(t,1),delete b[e],n&&y.length>0&&a(r);for(var u=0;u0}var v=null,y=[],b={},g=[],w=0,m={},E=!1;return{toString:t,whenDoneWith:s,hasWaiters:d,inTransaction:h,currentEventId:p,wrappedSubscribe:l,afterTransaction:i,soonButNotYet:e,isInTransaction:r}}();E.prototype.hasSubscribers=function(){return this.subscriptions.length>0},E.prototype.removeSub=function(t){return this.subscriptions=Tt.without(t,this.subscriptions),this.subscriptions},E.prototype.push=function(t){return t.isEnd&&(this.ended=!0),Bt.inTransaction(t,this,this.pushIt,[t])},E.prototype.pushToSubscriptions=function(t){try{for(var n=this.subscriptions,r=n.length,e=0;e1?n-1:0),e=1;e0&&void 0!==arguments[0]?arguments[0]:Qt;return w(new b(this,"skipDuplicates",[]),this.withStateMachine(It,function(n,r){return r.hasValue?r.isInitial||Ut(n)||!t(n.get(),r.value)?[new o(r.value),[r]]:[n,[]]:[n,[r]]}))},e(Q.prototype,{_isSource:!0,subscribe:function(t){return this.obs.dispatcher.subscribe(t)},toString:function(){return this.obs.toString()},markEnded:function(){return this.ended=!0,!0},consume:function(){return this.queue[0]},push:function(t){this.queue=[t]},mayHave:function(){return!0},hasAtLeast:function(){return this.queue.length},flatten:!0}),i(U,Q),e(U.prototype,{consume:function(){return this.queue.shift()},push:function(t){return this.queue.push(t)},mayHave:function(t){return!this.ended||this.queue.length>=t},hasAtLeast:function(t){return this.queue.length>=t},flatten:!1}),i(z,Q),e(z.prototype,{consume:function(){var t=this.queue;return this.queue=[],{value:t}},push:function(t){return this.queue.push(t.value)},hasAtLeast:function(){return!0}}),Q.isTrigger=function(t){return null!=t&&(t._isSource?t.sync:t._isEventStream)},Q.fromObservable=function(t){return null!=t&&t._isSource?t:null!=t&&t._isProperty?new Q(t,!1):new U(t,!0)},jt.when=R,jt.groupSimultaneous=K,P.prototype.awaiting=nt,jt.combineAsArray=function(){var t=rt(arguments);if(t.length){for(var n=[],r=0;r1&&void 0!==arguments[1]?arguments[1]:t,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t,i={scheduled:null,end:void 0,values:[],flush:function(){if(this.scheduled&&(jt.scheduler.clearTimeout(this.scheduled),this.scheduled=null),this.values.length>0){var t=this.values;this.values=[];var n=this.push(l(t));if(null!=this.end)return this.push(this.end);if(n!==Pt)return e(this)}else if(null!=this.end)return this.push(this.end)},schedule:function(){var t=this;if(!this.scheduled)return this.scheduled=n(function(){return t.flush()})}},u=Vt;if(!Tt.isFunction(n)){var o=n;n=function(t){return jt.scheduler.setTimeout(t,o)}}return w(new b(this,"buffer",[]),this.withHandler(function(t){var n=this;return i.push=function(t){return n.push(t)},t.isError?u=this.push(t):t.isEnd?(i.end=t,i.scheduled||i.flush()):(i.values.push(t.value),r(i)),u}))},P.prototype.filter=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;e1&&void 0!==arguments[1]?arguments[1]:{},r=this,e=[r],i=[],u=this._isProperty,o=u?H:C,s=!1,c=o(n.desc||new b(this,"flatMap_",arguments),function(e){var o=new F,c=[],f=function(n){if(u&&n.isInitial){if(s)return Vt;s=!0}var r=ot(t(n));return i.push(r),o.add(function(t,n){return r.dispatcher.subscribe(function(u){if(u.isEnd)return Tt.remove(r,i),a(),h(n),Pt;u=u.toNext();var o=e(u);return o===Pt&&t(),o})})},a=function(){var t=c.shift();if(t)return f(t)},h=function(t){if(t(),o.empty())return e(d())};return o.add(function(t,i){return r.dispatcher.subscribe(function(t){return t.isEnd?h(i):t.isError&&!n.mapError?e(t):n.firstOnly&&o.count()>1?Vt:o.unsubscribed?Pt:n.limit&&o.count()>n.limit?c.push(t):f(t)})}),o.unsubscribe});return c.internalDeps=function(){return i.length?e.concat(i):e},c};var zt=function(t){return function(n){return t(n.value)}};P.prototype.flatMapWithConcurrencyLimit=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;e1?i-1:0),o=1;o1?r-1:0),i=1;i1?r-1:0),i=1;i1?n-1:0),e=1;e0?this.push(n):(0===t&&this.push(n),this.push(d()),Pt)):this.push(n)}))},P.prototype.first=function(){return w(new b(this,"first",[]),this.take(1))},P.prototype.flatMapEvent=function(){return this.flatMap_(ut(arguments),{mapError:!0,desc:new b(this,"flatMapEvent",arguments)})},P.prototype.flatMapFirst=function(){return this.flatMap_(zt(ut(arguments)),{firstOnly:!0,desc:new b(this,"flatMapFirst",arguments)})},P.prototype.mapError=function(){var t=A(arguments);return w(new b(this,"mapError",[t]),this.withHandler(function(n){return n.isError?this.push(l(t(n.error))):this.push(n)}))},P.prototype.flatMapError=function(t){return this.flatMap_(function(n){return n instanceof h?t(n.error):n},{mapError:!0,desc:new b(this,"flatMapError",[t])})},P.prototype.flatScan=function(t,n){var r=t;return this.flatMapConcat(function(t){return ot(n(r,t)).doAction(function(t){return r=t})}).toProperty(t)},C.prototype.sampledBy=function(t,n){return w(new b(this,"sampledBy",[t,n]),this.toProperty().sampledBy(t,n))},D.prototype.sampledBy=function(t,n){n=void 0!==n&&null!==n?O(n):jt._.id;var r=new Q(this,!1),e=new Q(t,!0),i=t._isProperty?Y:R,u=i([r,e],n);return w(new b(this,"sampledBy",[t,n]),u)},D.prototype.sample=function(t){return w(new b(this,"sample",[t]),this.sampledBy(jt.interval(t,{})))},P.prototype.map=function(t){return t&&t._isProperty?t.sampledBy(this,n):j.apply(this,arguments)},P.prototype.fold=function(t,n){return w(new b(this,"fold",[t,n]),this.scan(t,n).sampledBy(this.filter(!1).mapEnd().toProperty()))},P.prototype.reduce=P.prototype.fold,jt.fromArray=function(t){if(t.length){var n=0,r=new C(new b(jt,"fromArray",[t]),function(e){function i(){if(c=!0,!s){for(s=!0;c;)if(c=!1,o!==Pt&&!u){var f=t[n++];o=e(v(f)),o!==Pt&&(n===t.length?e(d()):Bt.afterTransaction(r,i))}return s=!1}}var u=!1,o=Vt,s=!1,c=!1;return Bt.soonButNotYet(r,i),function(){return u=!0}});return r}return w(new b(jt,"fromArray",t),q())},jt.fromESObservable=function(t){var n;return n=t[u("observable")]?t[u("observable")]():t,new C(new b(jt,"fromESObservable",[n]),function(t){var r=n.subscribe({error:function(){t(new jt.Error),t(new jt.End)},next:function(n){t(new jt.Next(n,!0))},complete:function(){t(new jt.End)}});return r.unsubscribe?function(){r.unsubscribe()}:r})};var Yt=[["addEventListener","removeEventListener"],["addListener","removeListener"],["on","off"],["bind","unbind"]],$t=function(t){for(var n,r=0;r1&&void 0!==arguments[1]?arguments[1]:Tt.id,r={},e=this;return e.filter(function(n){return!r[t(n)]}).map(function(i){var u=t(i),o=e.filter(function(n){return t(n)===u}),s=it(i).concat(o),c=n(s,i).withHandler(function(t){if(this.push(t),t.isEnd)return delete r[u]});return r[u]=c,c})},C.prototype.holdWhen=function(t){var n=!1,r=[],e=this,i=!1;return new C(new b(this,"holdWhen",[t]),function(u){var o=new F,s=!1,c=function(t){if("function"==typeof t&&t(),o.empty()&&s)return u(d())};return o.add(function(e,o){return t.subscribeInternal(function(t){if(!t.hasValue)return t.isEnd?c(o):u(t);if(!(n=t.value)){var e=r;return r=[],function(){for(var t,n=[],r=0;r0?(t--,Vt):this.push(n)}))},C.prototype.skipUntil=function(t){var n=t.take(1).map(!0).toProperty(!1);return w(new b(this,"skipUntil",[t]),this.filter(n))},C.prototype.skipWhile=function(t){for(var n=!1,r=arguments.length,e=Array(r>1?r-1:0),i=1;i1&&void 0!==arguments[1]?arguments[1]:0;return w(new b(this,"slidingWindow",[t,n]),this.scan([],function(n,r){return n.concat([r]).slice(-t)}).filter(function(t){return t.length>=n}))},P.prototype.takeWhile=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;en;12?e-2:0),u=2;u1?r-1:0),i=1;i1&&"."===t.charAt(0)}function M(t,n){return Ct.apply(void 0,[t].concat(n))}function T(t,n,r,e){if(n&&n._isProperty){var i=n.sampledBy(t,function(t,n){return[t,n]});return e.call(i,function(t){return t[0]}).map(function(t){return t[1]})}return n=M(n,r),e.call(t,n)}function O(t){if(Tt.isFunction(t))return t;if(I(t)){var n=W(t);return function(t,r){return t[n](r)}}throw new Error("not a function or a field key: "+t)}function W(t){return t.slice(1)}function P(t){this.desc=t,this.id=++Ft,this.initialDesc=this.desc}function V(t,n,r){E.call(this,n,r),this.property=t,this.subscribe=Tt.bind(this.subscribe,this),this.current=It,this.currentValueRootId=void 0,this.propertyEnded=!1}function H(t,n){return new D(t,C(It,n))}function D(t,n,r){P.call(this,t),this.dispatcher=new V(this,n,r),y(this)}function N(t,n,r,e){if(!(this instanceof N))return new N(t,n,r);Tt.isFunction(t)&&(r=n,n=t,t=Lt),e!==qt&&(n=B(this,n)),P.call(this,t),this.dispatcher=new E(n,r),y(this)}function B(t,n){var r=!1;return function(e){function i(){var t=o;o=null;for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[];this.unsubscribe=Tt.bind(this.unsubscribe,this),this.unsubscribed=!1,this.subscriptions=[],this.starting=[];for(var n,r=0;r1?n-1:0),e=1;e0&&e.push(f),i+=2}return[r,e,u]}function Z(t,n){if(0===n.length)return q();var r=$(n),e=r[0],i=r[1],u=r[2];if(!e.length)return q();e=Tt.map(Q.fromObservable,e);var o=Tt.any(e,function(t){return t.flatten})&&X(Tt.map(function(t){return t.obs},e)),s=new b(jt,"when",u),c=t(s,function(t){function n(t){for(var n=0;n0))return Vt;for(var r,o=Vt,s=f.pop(),c=0;c1&&void 0!==arguments[1]?arguments[1]:[];return Tt.any(t,n)}function G(t){return Tt.isFunction(t)?t:Tt.always(t)}function J(t){return!t.sync||t.ended}function K(){for(var t=arguments.length,n=Array(t),r=0;r1&&void 0!==arguments[1]?arguments[1]:Tt.id;return new N(new b(jt,"fromBinder",[t,n]),function(r){var e=!1,i=!1,u=function(){if(!e)return void 0!==o&&null!==o?(o(),e=!0):i=!0},o=t(function(){for(var t,e=arguments.length,i=Array(e),o=0;o2&&void 0!==arguments[2]?arguments[2]:yt;return w(new b(jt,"fromPromise",[t]),ct(function(r){var e=t.then(r,function(t){return r(new h(t))});return e&&"function"==typeof e.done&&e.done(),n?function(){if("function"==typeof t.abort)return t.abort()}:function(){}},r))}function gt(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return w(new b(jt,"interval",[t,n]),vt(t,function(){return l(n)}))}function wt(){var t=rt(arguments);return t.length?new N(new b(jt,"mergeAll",t),function(n){var r=0,e=function(e){return function(i){return e.dispatcher.subscribe(function(e){if(e.isEnd)return r++,r===t.length?n(d()):Vt;var u=n(e);return u===Pt&&i(),u})}};return new F(Tt.map(e,t)).unsubscribe}):q()}function mt(t,n){var r=0;return w(new b(jt,"repeatedly",[t,n]),vt(t,function(){return n[r++%n.length]}))}function Et(t){var n=0;return ct(function(r){function e(t){return t.isEnd?u?i():u=!0:o=r(t)}function i(){var i;for(u=!0;u&&o!==Pt;)i=t(n++),u=!1,i?s=i.subscribeInternal(e):r(d());return u=!0}var u=!1,o=Vt,s=function(){};return i(),function(){return s()}})}function St(t,n){var r=0;return w(new b(jt,"sequentially",[t,n]),vt(t,function(){var t=n[r++];return r1?n-1:0),e=1;e0;)r[i]instanceof Function||(r[i]=Tt.always(r[i])),r[i]=function(t){return function(){for(var n=arguments.length,r=Array(n),e=0;e1&&void 0!==arguments[1]?arguments[1]:Tt.id,e=0;e1&&void 0!==arguments[1]?arguments[1]:Tt.id,e=0;e=0)return n.splice(r,1)},fold:function(t,n,r){for(var e,i=0;i5?"[..]":"["+Tt.map(Tt.toString,t).toString()+"]":null!=(null!=t?t.toString:void 0)&&t.toString!==Object.prototype.toString?t.toString():"object"==typeof t?Ot>5?"{..}":"{"+function(){var i=[];for(n in t)e.call(t,n)&&(r=function(){try{return t[n]}catch(t){return t}}(),i.push(Tt.toString(n)+":"+Tt.toString(r)));return i}()+"}":t}finally{Ot--}}},Ot=0,Wt=0;s.prototype._isEvent=!0,s.prototype.isEvent=!0,s.prototype.isEnd=!1,s.prototype.isInitial=!1,s.prototype.isNext=!1,s.prototype.isError=!1,s.prototype.hasValue=!1,s.prototype.filter=function(){return!0},s.prototype.inspect=function(){return this.toString()},s.prototype.log=function(){return this.toString()},s.prototype.toNext=function(){return this},i(c,s),c.prototype.isNext=!0,c.prototype.hasValue=!0,c.prototype.fmap=function(t){return this.apply(t(this.value))},c.prototype.apply=function(t){return new c(t)},c.prototype.filter=function(t){return t(this.value)},c.prototype.toString=function(){return Tt.toString(this.value)},c.prototype.log=function(){return this.value},c.prototype._isNext=!0,i(f,c),f.prototype._isInitial=!0,f.prototype.isInitial=!0,f.prototype.isNext=!1,f.prototype.apply=function(t){return new f(t)},f.prototype.toNext=function(){return new c(this.value)},i(a,s),a.prototype.isEnd=!0,a.prototype.fmap=function(){return this},a.prototype.apply=function(){return this},a.prototype.toString=function(){return""},i(h,s),h.prototype.isError=!0,h.prototype.fmap=function(){return this},h.prototype.apply=function(){return this},h.prototype.toString=function(){return" "+Tt.toString(this.error)};var Pt="",Vt="",Ht=[],Dt=function(t){return Ht.push(t)};e(b.prototype,{_isDesc:!0,deps:function(){return this.cached||(this.cached=m([this.context].concat(this.args))),this.cached},toString:function(){var t=Tt.map(Tt.toString,this.args);return Tt.toString(this.context)+"."+Tt.toString(this.method)+"("+t+")"}});var Nt={setTimeout:function(t,n){return setTimeout(t,n)},setInterval:function(t,n){return setInterval(t,n)},clearInterval:function(t){return clearInterval(t)},clearTimeout:function(t){return clearTimeout(t)},now:function(){return(new Date).getTime()}},Bt=function(){function t(){return Tt.toString({rootEvent:v,processingAfters:E,waiterObs:y,waiters:b,aftersStack:g,aftersStackHeight:w,flushed:m})}function n(t){t<=w||(g[t-1]||(g[t-1]=[[],0]),w=t)}function r(){return null!==v}function e(t,n){v?s(t,n):jt.scheduler.setTimeout(n,0)}function i(t,r){if(!v&&!E)return r();n(1);for(var e=0;e=t;){var e=g[w-1];if(!e)throw new Error("Unexpected stack top: "+e);var i=e[0],u=e[1];if(!(ut&&0==g[w-1][0].length;)w--}finally{c||(g=[],w=0)}}}finally{r&&(E=!1)}}}function s(t,n){if(v){var r=b[t.id];return void 0===r||null===r?(r=b[t.id]=[n],y.push(t)):r.push(n)}return n()}function c(){for(;y.length>0;)f(0,!0);m={}}function f(t,n){var r=y[t],e=r.id,i=b[e];y.splice(t,1),delete b[e],n&&y.length>0&&a(r);for(var u=0;u0}var v=null,y=[],b={},g=[],w=0,m={},E=!1;return{toString:t,whenDoneWith:s,hasWaiters:d,inTransaction:h,currentEventId:p,wrappedSubscribe:l,afterTransaction:i,soonButNotYet:e,isInTransaction:r}}();E.prototype.hasSubscribers=function(){return this.subscriptions.length>0},E.prototype.removeSub=function(t){return this.subscriptions=Tt.without(t,this.subscriptions),this.subscriptions},E.prototype.push=function(t){return t.isEnd&&(this.ended=!0),Bt.inTransaction(t,this,this.pushIt,[t])},E.prototype.pushToSubscriptions=function(t){try{for(var n=this.subscriptions,r=n.length,e=0;e1?n-1:0),e=1;e"};jt.Bacon=jt,P.prototype.map=j,P.prototype.withStateMachine=function(t,n){var r=t;return w(new b(this,"withStateMachine",[t,n]),this.withHandler(function(t){var e=n(r,t),i=e[0],u=e[1];r=i;for(var o,s=Vt,c=0;c0&&void 0!==arguments[0]?arguments[0]:Qt;return w(new b(this,"skipDuplicates",[]),this.withStateMachine(It,function(n,r){return r.hasValue?r.isInitial||Ut(n)||!t(n.get(),r.value)?[new o(r.value),[r]]:[n,[]]:[n,[r]]}))},e(Q.prototype,{_isSource:!0,subscribe:function(t){return this.obs.dispatcher.subscribe(t)},toString:function(){return this.obs.toString()},markEnded:function(){return this.ended=!0,!0},consume:function(){return this.queue[0]},push:function(t){this.queue=[t]},mayHave:function(){return!0},hasAtLeast:function(){return this.queue.length},flatten:!0}),i(U,Q),e(U.prototype,{consume:function(){return this.queue.shift()},push:function(t){return this.queue.push(t)},mayHave:function(t){return!this.ended||this.queue.length>=t},hasAtLeast:function(t){return this.queue.length>=t},flatten:!1}),i(z,Q),e(z.prototype,{consume:function(){var t=this.queue;return this.queue=[],{value:t}},push:function(t){return this.queue.push(t.value)},hasAtLeast:function(){return!0}}),Q.isTrigger=function(t){return null!=t&&(t._isSource?t.sync:t._isEventStream)},Q.fromObservable=function(t){return null!=t&&t._isSource?t:null!=t&&t._isProperty?new Q(t,!1):new U(t,!0)},jt.when=R,jt.groupSimultaneous=K,P.prototype.awaiting=nt,jt.combineAsArray=function(){var t=rt(arguments);if(t.length){for(var n=[],r=0;r1&&void 0!==arguments[1]?arguments[1]:t,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t,i={scheduled:null,end:void 0,values:[],flush:function(){if(this.scheduled&&(jt.scheduler.clearTimeout(this.scheduled),this.scheduled=null),this.values.length>0){var t=this.values;this.values=[];var n=this.push(l(t));if(null!=this.end)return this.push(this.end);if(n!==Pt)return e(this)}else if(null!=this.end)return this.push(this.end)},schedule:function(){var t=this;if(!this.scheduled)return this.scheduled=n(function(){return t.flush()})}},u=Vt;if(!Tt.isFunction(n)){var o=n;n=function(t){return jt.scheduler.setTimeout(t,o)}}return w(new b(this,"buffer",[]),this.withHandler(function(t){var n=this;return i.push=function(t){return n.push(t)},t.isError?u=this.push(t):t.isEnd?(i.end=t,i.scheduled||i.flush()):(i.values.push(t.value),r(i)),u}))},P.prototype.filter=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;e1&&void 0!==arguments[1]?arguments[1]:{},r=this,e=[r],i=[],u=this._isProperty,o=u?H:N,s=!1,c=o(n.desc||new b(this,"flatMap_",arguments),function(e){var o=new F,c=[],f=function(n){if(u&&n.isInitial){if(s)return Vt;s=!0}var r=ot(t(n));return i.push(r),o.add(function(t,n){return r.dispatcher.subscribe(function(u){if(u.isEnd)return Tt.remove(r,i),a(),h(n),Pt;u=u.toNext();var o=e(u);return o===Pt&&t(),o})})},a=function(){var t=c.shift();if(t)return f(t)},h=function(t){if(t(),o.empty())return e(d())};return o.add(function(t,i){return r.dispatcher.subscribe(function(t){return t.isEnd?h(i):t.isError&&!n.mapError?e(t):n.firstOnly&&o.count()>1?Vt:o.unsubscribed?Pt:n.limit&&o.count()>n.limit?c.push(t):f(t)})}),o.unsubscribe});return c.internalDeps=function(){return i.length?e.concat(i):e},c};var zt=function(t){return function(n){return t(n.value)}};P.prototype.flatMapWithConcurrencyLimit=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;e1?i-1:0),o=1;o1?r-1:0),i=1;i1?r-1:0),i=1;i1?n-1:0),e=1;e0?this.push(n):(0===t&&this.push(n),this.push(d()),Pt)):this.push(n)}))},P.prototype.first=function(){return w(new b(this,"first",[]),this.take(1))},P.prototype.flatMapEvent=function(){return this.flatMap_(ut(arguments),{mapError:!0,desc:new b(this,"flatMapEvent",arguments)})},P.prototype.flatMapFirst=function(){return this.flatMap_(zt(ut(arguments)),{firstOnly:!0,desc:new b(this,"flatMapFirst",arguments)})},P.prototype.mapError=function(){var t=A(arguments);return w(new b(this,"mapError",[t]),this.withHandler(function(n){return n.isError?this.push(l(t(n.error))):this.push(n)}))},P.prototype.flatMapError=function(t){return this.flatMap_(function(n){return n instanceof h?t(n.error):n},{mapError:!0,desc:new b(this,"flatMapError",[t])})},P.prototype.flatScan=function(t,n){var r=t;return this.flatMapConcat(function(t){return ot(n(r,t)).doAction(function(t){return r=t})}).toProperty(t)},N.prototype.sampledBy=function(t,n){return w(new b(this,"sampledBy",[t,n]),this.toProperty().sampledBy(t,n))},D.prototype.sampledBy=function(t,n){n=void 0!==n&&null!==n?O(n):jt._.id;var r=new Q(this,!1),e=new Q(t,!0),i=t._isProperty?Y:R,u=i([r,e],n);return w(new b(this,"sampledBy",[t,n]),u)},D.prototype.sample=function(t){return w(new b(this,"sample",[t]),this.sampledBy(jt.interval(t,{})))},P.prototype.map=function(t){return t&&t._isProperty?t.sampledBy(this,n):j.apply(this,arguments)},P.prototype.fold=function(t,n){return w(new b(this,"fold",[t,n]),this.scan(t,n).sampledBy(this.filter(!1).mapEnd().toProperty()))},P.prototype.reduce=P.prototype.fold,jt.fromArray=function(t){if(t.length){var n=0,r=new N(new b(jt,"fromArray",[t]),function(e){function i(){if(c=!0,!s){for(s=!0;c;)if(c=!1,o!==Pt&&!u){var f=t[n++];o=e(v(f)),o!==Pt&&(n===t.length?e(d()):Bt.afterTransaction(r,i))}return s=!1}}var u=!1,o=Vt,s=!1,c=!1;return Bt.soonButNotYet(r,i),function(){return u=!0}});return r}return w(new b(jt,"fromArray",t),q())},jt.fromESObservable=function(t){var n;return n=t[u("observable")]?t[u("observable")]():t,new N(new b(jt,"fromESObservable",[n]),function(t){var r=n.subscribe({error:function(){t(new jt.Error),t(new jt.End)},next:function(n){t(new jt.Next(n,!0))},complete:function(){t(new jt.End)}});return r.unsubscribe?function(){r.unsubscribe()}:r})};var Yt=[["addEventListener","removeEventListener"],["addListener","removeListener"],["on","off"],["bind","unbind"]],$t=function(t){for(var n,r=0;r1&&void 0!==arguments[1]?arguments[1]:Tt.id,r={},e=this;return e.filter(function(n){return!r[t(n)]}).map(function(i){var u=t(i),o=e.filter(function(n){return t(n)===u}),s=it(i).concat(o),c=n(s,i).withHandler(function(t){if(this.push(t),t.isEnd)return delete r[u]});return r[u]=c,c})},N.prototype.holdWhen=function(t){var n=!1,r=[],e=this,i=!1;return new N(new b(this,"holdWhen",[t]),function(u){var o=new F,s=!1,c=function(t){if("function"==typeof t&&t(),o.empty()&&s)return u(d())};return o.add(function(e,o){return t.subscribeInternal(function(t){if(!t.hasValue)return t.isEnd?c(o):u(t);if(!(n=t.value)){var e=r;return r=[],function(){for(var t,n=[],r=0;r0?(t--,Vt):this.push(n)}))},N.prototype.skipUntil=function(t){var n=t.take(1).map(!0).toProperty(!1);return w(new b(this,"skipUntil",[t]),this.filter(n))},N.prototype.skipWhile=function(t){for(var n=!1,r=arguments.length,e=Array(r>1?r-1:0),i=1;i1&&void 0!==arguments[1]?arguments[1]:0;return w(new b(this,"slidingWindow",[t,n]),this.scan([],function(n,r){return n.concat([r]).slice(-t)}).filter(function(t){return t.length>=n}))},P.prototype.takeWhile=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),e=1;e1&&void 0!==arguments[1]?arguments[1]:Zt,u=i.leading,o=i.trailing,s=0,c=function(){null!==r&&(jt.scheduler.clearTimeout(r),r=null)},f=void 0,a=function(){f.dispatcher.push(n),r=null,n=null,s=jt.scheduler.now(),e&&f.dispatcher.push(e)};return f=w(new b(this,"throttle",i===Zt?[t]:[t,i]),this.withHandler(function(i){if(i.isNext){var f=jt.scheduler.now();0!==s||u||(s=f);var h=t-(f-s);if(h<=0)return c(),s=f,this.push(i);o&&(n=i,c(),r=jt.scheduler.setTimeout(a,h))}else{if(!i.isEnd||null===r)return this.push(i);e=i}}))},D.prototype.toEventStream=function(t){var n=this;return new N(new b(this,"toEventStream",[]),function(t){return n.dispatcher.subscribe(function(n){return t(n.toNext())})},null,t)},P.prototype.firstToPromise=function(t){var n=this;if("function"!=typeof t){if("function"!=typeof Promise)throw new Error("There isn't default Promise, use shim or parameter");t=Promise}return new t(function(t,r){return n.subscribe(function(n){return n.hasValue&&t(n.value),n.isError&&r(n.error),Pt})})},P.prototype.toPromise=function(t){return this.last().firstToPromise(t)},jt.try=At,jt.update=kt,jt.zipAsArray=function(){for(var t=arguments.length,n=Array(t),r=0;r' }; Bacon.Bacon = Bacon; function map(p) { @@ -3264,12 +3264,53 @@ })); }); }; + var defaultOptions = { trailing: true }; Observable.prototype.throttle = function (delay) { - return this.delayChanges(new Desc(this, 'throttle', [delay]), function (changes) { - return changes.bufferWithTime(delay).map(function (values) { - return values[values.length - 1]; - }); - }); + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOptions; + var leading = options.leading, trailing = options.trailing; + var lastCallTime = 0; + var trailingValue, timeoutId, trailingEnd; + var cancelTrailing = function () { + if (timeoutId !== null) { + Bacon.scheduler.clearTimeout(timeoutId); + timeoutId = null; + } + }; + var stream = void 0; + var trailingCall = function () { + stream.dispatcher.push(trailingValue); + timeoutId = null; + trailingValue = null; + lastCallTime = Bacon.scheduler.now(); + if (trailingEnd) { + stream.dispatcher.push(trailingEnd); + } + }; + return stream = withDesc(new Desc(this, 'throttle', options === defaultOptions ? [delay] : [ + delay, + options + ]), this.withHandler(function (event) { + if (event.isNext) { + var curTime = Bacon.scheduler.now(); + if (lastCallTime === 0 && !leading) { + lastCallTime = curTime; + } + var remaining = delay - (curTime - lastCallTime); + if (remaining <= 0) { + cancelTrailing(); + lastCallTime = curTime; + return this.push(event); + } else if (trailing) { + trailingValue = event; + cancelTrailing(); + timeoutId = Bacon.scheduler.setTimeout(trailingCall, remaining); + } + } else if (event.isEnd && timeoutId !== null) { + trailingEnd = event; + } else { + return this.push(event); + } + })); }; Property.prototype.toEventStream = function (options) { var _this = this; @@ -3371,4 +3412,4 @@ ], f || Array)); }; return Bacon; -})); +})); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4079aec0e..2cbd674c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "baconjs", - "version": "2.0.4", + "version": "2.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/spec/specs/throttle.coffee b/spec/specs/throttle.coffee index 7036a72df..49fe1c968 100644 --- a/spec/specs/throttle.coffee +++ b/spec/specs/throttle.coffee @@ -21,6 +21,15 @@ describe "EventStream.throttle(delay)", -> expectStreamTimings( -> series(2, [1, 2, 3]).throttle(t(3)) [[5, 2], [8, 3]]) + describe "options", -> + describe "{ leading: true }", -> + expectStreamTimings( + -> series(2, [1, 2, 3]).throttle(t(3), { leading: true }) + [[2, 1], [6, 3]]) + describe "{ trailing: true, leading: true }", -> + expectStreamTimings( + -> series(2, [1, 2, 3]).throttle(t(3), { leading: true, trailing: true }) + [[2, 1], [5, 2], [8, 3]]) describe "works with synchronous source", -> expectStreamEvents( -> fromArray([1, 2, 3]).throttle(t(3)) diff --git a/src/throttle.js b/src/throttle.js index 419ab1e07..b1e0a0c0b 100644 --- a/src/throttle.js +++ b/src/throttle.js @@ -1,9 +1,56 @@ -import "./buffer"; -import "./delaychanges"; -import "./map"; +import "./scheduler"; +import Bacon from "./core"; import Observable from "./observable"; -import { Desc } from "./describe"; +import { Desc, withDesc } from "./describe"; -Observable.prototype.throttle = function (delay) { - return this.delayChanges(new Desc(this, "throttle", [delay]), (changes) => changes.bufferWithTime(delay).map((values) => values[values.length - 1])); +const defaultOptions = { trailing: true } + +Observable.prototype.throttle = function (delay, options = defaultOptions ) { + var { leading, trailing } = options + var lastCallTime = 0 + var trailingValue, timeoutId, trailingEnd + const cancelTrailing = () => { + if (timeoutId !== null) { + Bacon.scheduler.clearTimeout(timeoutId) + timeoutId = null + } + } + let stream + const trailingCall = () => { + //console.log("delayed push", trailingValue, trailingEnd) + stream.dispatcher.push(trailingValue) + timeoutId = null + trailingValue = null + lastCallTime = Bacon.scheduler.now() + if (trailingEnd) { + stream.dispatcher.push(trailingEnd) + } + } + return stream = withDesc( + new Desc(this, "throttle", options === defaultOptions ? [delay] : [delay, options]), + this.withHandler(function (event) { + if (event.isNext) { + let curTime = Bacon.scheduler.now() + if (lastCallTime === 0 && !leading) { + lastCallTime = curTime + } + let remaining = delay - (curTime - lastCallTime) + if (remaining <= 0) { + cancelTrailing() + lastCallTime = curTime + return this.push(event) + } else if (trailing) { + //console.log('scheduling', event, 'in', remaining) + trailingValue = event + cancelTrailing() + timeoutId = Bacon.scheduler.setTimeout(trailingCall, remaining) + } + } else if (event.isEnd && timeoutId !== null) { + trailingEnd = event + } else { + //console.log('immediate', event) + return this.push(event) + } + }) + ) };