How do I make an action occur with a delay, but after a timeout?
The setTimeout() function doesn’t work in Decentraland scenes, so is there an alternative?
For example, I want an entity to wait 300 milliseconds after it’s clicked before I remove it from the engine.
To implement this you’ll have to create:
A custom component to keep track of time
A component group to keep track of all the entities with a delay in the scene
A system that updates the timers con all these
components on each frame.
It sounds rather complicated, but once you created one delay, implementing another delay only takes one line.
The component:
#Component("timerDelay")
export class Delay implements ITimerComponent{
elapsedTime: number;
targetTime: number;
onTargetTimeReached: (ownerEntity: IEntity) => void;
private onTimeReachedCallback?: ()=> void
/**
* #param millisecs amount of time in milliseconds
* #param onTimeReachedCallback callback for when time is reached
*/
constructor(millisecs: number, onTimeReachedCallback?: ()=> void){
this.elapsedTime = 0
this.targetTime = millisecs / 1000
this.onTimeReachedCallback = onTimeReachedCallback
this.onTargetTimeReached = (entity)=>{
if (this.onTimeReachedCallback) this.onTimeReachedCallback()
entity.removeComponent(this)
}
}
}
The component group:
export const delayedEntities = engine.getComponentGroup(Delay)
The system:
// define system
class TimerSystem implements ISystem {
update(dt: number){
for (let entity of delayedEntities.entities) {
let timerComponent = entity.getComponent(component)
timerComponent.elapsedTime += dt
if (timerComponent.elapsedTime >= timerComponent.targetTime){
timerComponent.onTargetTimeReached(entity)
}
})
}
}
// instance system
engine.addSystem(new TimerSystem())
Once all these parts are in place, you can simply do the following to delay an execution in your scene:
const myEntity = new Entity()
myEntity.addComponent(new Delay(1000, () => {
log("time ran out")
}))
engine.addEntity(myEntity)
A few years late, but the OP's selected answer is kind of deprecated because you can accomplish a delay doing:
import { Delay } from "node_modules/decentraland-ecs-utils/timer/component/delay"
const ent = new Entity
ent.addComponent(new Delay(3 * 1000, () => {
// this code will run when time is up
}))
Read the docs.
Use the utils.Delay() function in the utils library.
This function just takes the delay time in milliseconds, and the function you want to execute.
Here's the full documentation, explaining how to add the library + how to use this function, including example code:
https://www.npmjs.com/package/decentraland-ecs-utils
Related
I'm looking for some examples of usage of Triggers and Timers in Apache beam, I wanted to use Processing-time timers for listening my data from pub sub in every 5 minutes and using Processing time triggers processing the above data collected in an hour altogether in python.
Please take a look at the following resources: Stateful processing with Apache Beam and Timely (and Stateful) Processing with Apache Beam
The first blog post is more general in how to handle states for context, and the second has some examples on buffering and triggering after a certain period of time, which seems similar to what you are trying to do.
A full example was requested. Here is what I was able to come up with:
PCollection<String> records =
pipeline.apply(
"ReadPubsub",
PubsubIO.readStrings()
.fromSubscription(
"projects/{project}/subscriptions/{subscription}"));
TupleTag<Iterable<String>> every5MinTag = new TupleTag<>();
TupleTag<Iterable<String>> everyHourTag = new TupleTag<>();
PCollectionTuple timersTuple =
records
.apply("WithKeys", WithKeys.of(1)) // A KV<> is required to use state. Keying by data is more appropriate than hardcode.
.apply(
"Batch",
ParDo.of(
new DoFn<KV<Integer, String>, Iterable<String>>() {
#StateId("buffer5Min")
private final StateSpec<BagState<String>> bufferedEvents5Min =
StateSpecs.bag();
#StateId("count5Min")
private final StateSpec<ValueState<Integer>> countState5Min =
StateSpecs.value();
#TimerId("every5Min")
private final TimerSpec every5MinSpec =
TimerSpecs.timer(TimeDomain.PROCESSING_TIME);
#StateId("bufferHour")
private final StateSpec<BagState<String>> bufferedEventsHour =
StateSpecs.bag();
#StateId("countHour")
private final StateSpec<ValueState<Integer>> countStateHour =
StateSpecs.value();
#TimerId("everyHour")
private final TimerSpec everyHourSpec =
TimerSpecs.timer(TimeDomain.PROCESSING_TIME);
#ProcessElement
public void process(
#Element KV<Integer, String> record,
#StateId("count5Min") ValueState<Integer> count5MinState,
#StateId("countHour") ValueState<Integer> countHourState,
#StateId("buffer5Min") BagState<String> buffer5Min,
#StateId("bufferHour") BagState<String> bufferHour,
#TimerId("every5Min") Timer every5MinTimer,
#TimerId("everyHour") Timer everyHourTimer) {
if (Objects.firstNonNull(count5MinState.read(), 0) == 0) {
every5MinTimer
.offset(Duration.standardMinutes(1))
.align(Duration.standardMinutes(1))
.setRelative();
}
buffer5Min.add(record.getValue());
if (Objects.firstNonNull(countHourState.read(), 0) == 0) {
everyHourTimer
.offset(Duration.standardMinutes(60))
.align(Duration.standardMinutes(60))
.setRelative();
}
bufferHour.add(record.getValue());
}
#OnTimer("every5Min")
public void onTimerEvery5Min(
OnTimerContext context,
#StateId("buffer5Min") BagState<String> bufferState,
#StateId("count5Min") ValueState<Integer> countState) {
if (!bufferState.isEmpty().read()) {
context.output(every5MinTag, bufferState.read());
bufferState.clear();
countState.clear();
}
}
#OnTimer("everyHour")
public void onTimerEveryHour(
OnTimerContext context,
#StateId("bufferHour") BagState<String> bufferState,
#StateId("countHour") ValueState<Integer> countState) {
if (!bufferState.isEmpty().read()) {
context.output(everyHourTag, bufferState.read());
bufferState.clear();
countState.clear();
}
}
})
.withOutputTags(every5MinTag, TupleTagList.of(everyHourTag)));
timersTuple
.get(every5MinTag)
.setCoder(IterableCoder.of(StringUtf8Coder.of()))
.apply(<<do something every 5 min>>);
timersTuple
.get(everyHourTag)
.setCoder(IterableCoder.of(StringUtf8Coder.of()))
.apply(<< do something every hour>>);
pipeline.run().waitUntilFinish();
I've recently started looking into the nice ember-resources library because I came to the point my data fetching routine required some reactivity. Since we tend to use the ember-concurrency tasks in our project I wanted to follow the common pattern and I was happy to realise that ember-resources support ember-concurrency tasks out of the box.
Now i've got a dependant tracked property which is basically a timer and it's being updated every minute. What I want to do is to be able to run my task say every two minutes. The question is how to achieve this?
Here goes my pseudo-code:
// that's my component
#restartableTask
*fetchFeed() {
yield timeout(1);
return yield this.store.queryRecord('item', {...});
}
get currentTime() {
// this one returns a tracked variable which is updated every minute
}
feedResource = trackedTask(this, this.fetchFeed, () => [this.currentTime]);
// this is the corresponding hbs
{{#if this.feedResource.isRunning}}
<LoadingSpinner />
{{else}}
{{this.feedResource.value}}
{{/if}}
what I want to do is basically
get every2Minutes() {
return Math.trunc(this.currentTime.second() / 2)
}
feedResource = trackedTask(this, this.fetchFeed, () => [this.every2Minutes]);
but it's still run on every minute. I've tried using the #cached attribute and a custom cache solution but it didn't help - despite that the cache gave me the correct value, i.e. only even ones, the task was still fired every minute. Can I tell the trackedTask to not fire if the dependency wasn't changed?
👋 Hi, I'm the author of ember-resources! glad you're having fun with the library!
this one returns a tracked variable which is updated every minute
While this is clever, if the variable isn't used in the task, it "feels weird". I don't have better words for this. 🙃
But, to directly answer your question, I'd define currentTime like this:
const ClockEveryOtherMinute = resource(({ on }) => {
let time = cell(new Date());
let interval = setInterval(
() => time.current = new Date(),
2 * 60 * 1000, // 2 minutes
);
on.cleanup(() => clearInterval(interval));
return () => time.current;
});
class Foo {
// `#use` is required when a resource returns a single value
#use currentTime = ClockEveryOtherMinute;
feedResource = trackedTask(this, this.fetchFeed, () => [this.currentTime]);
// ...etc
}
Here is an interactive demo of something very similar
However, I think there may be a more ergonomic way --
In a scenario where you want recurring behavior, there are a couple approaches you could take:
invoke the task once and use a while loop to do something periodically
use a constructor/destructor combo with setInterval
These are somewhat separate from the trackedTask helper utility, as the trackedTask helper utility "only" does lazy invocation of the .perform method on a task when a property on the TaskInstance would be accessed. So... I'm not actually sure if your getter's return would be updated if the task is re-performed every couple minutes manually. I'm maybe.. 60% sure it would? (this is something I don't have tests for).
Anywho back to the options:
invoke the task once
class Foo extends Component {
#tracked items;
#task
*fetchFeed() {
yield timeout(1);
while(true) {
this.items = yield this.store.queryRecord('item', {...});
yield timeout(1000 * 60 * 2); // 2 minutes
}
}
get hasItems() {
return Boolean(this.items?.length);
}
}
// this is the corresponding hbs
{{#if this.hasItems}}
{{this.items}}
{{else}}
<LoadingSpinner />
{{/if}}
using setInterval
import { registerDestructor } from '#ember/destroyable';
class Foo extends Component {
#tracked items;
constructor(owner, args) {
super(owner, args);
let interval = setInterval(() => {
if (isDestroyed(this) || isDestroying(this)) return;
this.fetchFeed.perform();
}, 2 * 60 * 1000);
registerDestructor(this, () => {
clearTimeout(interval);
});
// initial perform
this.fetchFeed.perform();
}
#task
*fetchFeed() {
yield timeout(1);
return yield this.store.queryRecord('item', {...});
}
get hasItems() {
return Boolean(this.fetchFeed.lastSuccessful?.value?.length);
}
}
// this is the corresponding hbs
{{#if this.hasItems}}
{{this.fetchFeed.lastSuccessful.value}}
{{else}}
<LoadingSpinner />
{{/if}}
A "do something every 2 minutes API"
I'm 🤷♂️ on this approach, but for completeness, it's probably reasonable to know that it's possible (I'm going to hand-wave over the implementation, as that could be other stack-overflow questions):
const doEveryTwoMinutes = resourceFactory((callback) => {
// ...
return resource(({ on }) => {
// ...
});
});
class Foo extends Component {
#use feed = doEveryTwoMinutes(() => {
return this.fetchFeed.perform(); // returns a task
});
#task
*fetchFeed() {
yield timeout(1);
return yield this.store.queryRecord('item', {...});
}
get hasItems() {
return Boolean(this.feed.length);
}
}
// this is the corresponding hbs
{{#if this.hasItems}}
{{this.feed}}
{{else}}
<LoadingSpinner />
{{/if}}
(no trackedTask needed, we rely on the internal tracked-state of a TaskInstance)
Why do these approaches not need trackedTask?
because we have "events" that we know about that cause the task to be performed -- and ember-concurrency is really good at being "an event handler", of sorts -- whereas ember-resources is more about deriving data (maybe eventually), with cleanup.
I have a Stream<T> in my flutter app which uses timeout() under hood. Everything works fine, but running unit tests takes a while, since they have to wait every time for the timeout.
As I can see from sources timeout() implementation is based on Timer(). Is there any way to artificially advance time for Timer() on unit test environment?
For example in RxJava there is TestScheduler which has methods like advanceTimeBy/advanceTimeTo. I am looking for similar functionality.
Update:
The code could look like this:
extension StreamEx<T> on Stream<T> {
/// timeout only the first emission
Stream<T> timeoutFirst(Duration timeLimit) =>
MergeStream([
take(1).timeout(timeLimit),
skip(1),
]);
}
My requirement for the test here is that its runtime should not depend on whatever passed as timeLimit to the method. It possible with RxJava but how to fix it at Darts streams.
Update:
The test could look like this:
void main() async {
group(
'StreamEx.timeoutFirst',
() {
const TIMEOUT = Duration(milliseconds: 100);
const DELAY = Duration(milliseconds: 110);
test(
'should throw `TimeoutException` if it took too long for the first emission',
() async {
final subject = BehaviorSubject<int>();
final stream = subject.timeoutFirst(TIMEOUT).dump('tag0');
expectLater(
stream,
emitsInOrder([
emitsError(isInstanceOf<TimeoutException>()),
emits(equals(1)),
emits(equals(2)),
]),
);
await Future.delayed(DELAY);
subject.add(1);
subject.add(2);
subject.close();
},
);
},
);
}
I've hit a bit of an interesting road block in my attempt at writing unit tests for some middleware as I can't seem to come up with a feasible means to fake two concurrent connections for a generator function which is a piece of koa middleware.
I have a constructor function that takes some setup options and returns a generator. This generator has access to some variables via closure which increment per request and decrement when the complete. Here is a subset of the code to give you an idea of what i'm trying to accomplish.
module.exports = function (options = {}) {
let connections = 0;
let {
max = 100
...
} = options;
return function *() {
connections++
...
if (connections > max) {
connections--;
// callback here
}
...
}
}
In simple terms I want to be able to keep track of multiple simultaneous "connections" in which I fire a callback when a max number of requests have been met. However, in my test i get back a single instance of this generator and can only call it once mimicking a single request, thus i can never meet the connections > max conditional
it("Should trigger callback when max connections reached", () => {
const gen = middleware({
max: 1,
onMax: function (current, max) {
this.maxReached = true;
}
}).call(context);
gen.next();
expect(context.maxReached).to.be.true;
});
Sometimes you just need a good night sleep to dream your answer. This was simply a matter of calling the same generator with two different contexts that represented two different requests and store a value to tests against on the latter. The counter would still increment because I never returned up the middleware chain (response) in order to decrement. It's more of a fake concurrency.
const middleware = limiter({
max: 1,
onMax: function (current, max) {
this.maxReached = true;
}
});
middleware.call(reqContext).next();
middleware.call(secondReqContext).next();
expect(secondReqContext.maxReached).to.be.true;
I have a set of Akka Actors and I give about a couple of hundreds of messages to each one of them. I want to track how much time each instance of that Actor took to process all the messages that it received. What I'm doing currently is to have a state in the Actor instance as:
var startTime
var firstCall
I set both the variables when the Actor instance is first called. Is there another way that I could use to track the processing time for my Actor instances? I want to avoid having a local state in my Actor instance.
This is a good use case for context.become.
Remember than a receive block in an Akka actor is just a PartialFunction[Any, Unit], so we can wrap that in another partial function. This is the same approach taken by Akka's builtin LoggingReceive.
class TimingReceive(r: Receive, totalTime: Long)(implicit ctx: ActorContext) extends Receive {
def isDefinedAt(o: Any): Boolean = {
r.isDefinedAt(o)
}
def apply(o: Any): Unit = {
val startTime = System.nanoTime
r(o)
val newTotal = totalTime + (System.nanoTime - startTime)
log.debug("Total time so far: " + totalTime + " nanoseconds")
ctx.become(new TimingReceive(r, newTotal))
}
}
object TimingReceive {
def apply(r: Receive)(implicit ctx: ActorContext): Receive = new TimingReceive(r, 0)
}
Then you can use it like this:
class FooActor extends Actor {
def receive = TimingReceive {
case x: String => println("got " + x)
}
}
After each message, the actor will log the time taken so far. Of course, if you want to do something else with that variable, you'll have to adapt this.
This approach doesn't measure the time the actor is alive of course, only the time taken to actually process messages. Nor will it be accurate if your receive function creates a future.