admin管理员组文章数量:1292172
What is the best and DRYest way to write code that can be executed either when some time has passed (say, 5 seconds) or some condition is met (e.g. bool = true
) - whichever es first. The five seconds start counting from when the script first ran, and the boolean is a global that is changed by another function. I don't think you can bine the time out and the bool-check in one statement, but another good way is also good.
Pseudo code:
if (bool = true OR timePassed = 5000):
runCode()
What is the best and DRYest way to write code that can be executed either when some time has passed (say, 5 seconds) or some condition is met (e.g. bool = true
) - whichever es first. The five seconds start counting from when the script first ran, and the boolean is a global that is changed by another function. I don't think you can bine the time out and the bool-check in one statement, but another good way is also good.
Pseudo code:
if (bool = true OR timePassed = 5000):
runCode()
Share
Improve this question
asked May 1, 2016 at 20:08
Bram VanroyBram Vanroy
28.6k26 gold badges147 silver badges265 bronze badges
6
- Every second check if the condition is true? – Edward Commented May 1, 2016 at 20:12
- @Edward Seems overkill. Must be a better way. – Bram Vanroy Commented May 1, 2016 at 20:13
- use a flag , which ever condition runs first changes the flag , and the function won't run nomore – maioman Commented May 1, 2016 at 20:15
-
@bram you can try the
obj.watch()
method that will alert you when a property has changed I'm not sure how cross browser the solution is. – Edward Commented May 1, 2016 at 20:19 - try this , as dry as it gets; – maioman Commented May 1, 2016 at 20:27
6 Answers
Reset to default 3None of the answers actually provide a full answer to the question, namely the whichever es first is not implemented -- or the final code is run twice.
You need a timer, and a condition (as other answers suggested but failed to bine in one whole).
var done = false;
var thisTimeout = setTimeout(function() {
myFunction();
}, 1000);
if ((someCondition) && !done) {
myFunction();
}
function myFunction() {
clearTimeout(thisTimeout);
done = true;
// Do stuff
}
You can set a timeout and cancel it if the function is called before the time limit is reached.
var timeout = setTimeout(function() {
runCode();
}, 5000);
function runCode() {
clearTimeout(timeout);
...
}
Edit: Now that I think of it, a better way to set the timeout in this instance would be
var timeout = setTimeout(runCode, 5000);
The following code uses two global variables, condition
and seconds
. The timer runs every second and increases seconds by 1 if condition
is not true
or seconds
are not more than 4.
condition = false // global
seconds = 0 // global
var timer = setInterval(function() {
if (condition || seconds > 4) {
clearInterval(timer)
}
seconds+=1;
}, 1000);
window.onload = function(){
var timer = setTimeout(RunCode, 5000);
function RunCode(){
//do something
//RunCode() already done and we don't want to run it second time
element.onevent = function(){};
}
//pseudo code
element.onevent = function(){
clearTimeout(timer);
RunCode();
}
//possibly more event handlers with similar logic
}
To solve this kind of problem in android, I came up with a construct caller Barrier. It executes the code either when the condition is met or a SLA is crossed( the timeout that you want ).
To build a barrier with your needs, do this,
Barrier b = Barrier.with(() -> false).withSLA(5000, true).startSLA();
There will be a point in the code base where you want to check whether some condition is met or not? Ex, if you want to check whether the API call is finished, place it in the response code of that.
b.strike()
Below is the implementation,
/**
* Barrier creates a condition based barrier, which prevents a piece of code from being executed
* till a condition represented by {@link Condition} bees true. It has an optional SLA
* that executes the code, if that SLA(in millis) expires only if execution did not take place due
* to the condition getting satisfied. It works in "which ever happens first" basis.
* If the condition gets satisfied first, then SLA will be ignored. And if SLA expires and then the condition
* gets satisfied after that then only SLA expiration execution will take place.
* Once a barrier is broken and {@link BarrierCode} is executed, it will not execute again for the
* same barrier instance. Barrier is a one time use class.
*/
public final class Barrier {
public static final String EXECUTED_DUE_TO_SLA = "sla";
public static final String EXECUTED_DUE_TO_CONDITION = "condition";
private static final String TAG = Barrier.class.getSimpleName();
// This is only there for tests, as there has to be a way to know the type of execution happened
// Whether it was due to SLA breach or condition getting satisfied.
@VisibleForTesting
AtomicReference<String> mExecutionMechanism = new AtomicReference<>(null);
private final WeakReference<Condition> mCondition;
private final AtomicBoolean mHasExecuted;
private final AtomicBoolean mSlaStarted = new AtomicBoolean(false);
private long mSLA = -1;
private BarrierCode mCode;
private boolean mShouldPostOnMainThread;
private Barrier(WeakReference<Condition> condition) {
this.mCondition = condition;
mHasExecuted = new AtomicBoolean(false);
}
/**
* Creates a Barrier object with a given condition.
*
* @param condition condition used to break the barrier.
* @return Barrier object
*/
public static Barrier with(@NonNull Condition condition) {
WeakReference<Condition> temp = new WeakReference<>(condition);
return new Barrier(temp);
}
public boolean hasBarrierFinishedExecution() {
return mHasExecuted.get();
}
/**
* Takes the code that needs to be executed when the barrier breaks due to condition getting
* satisfied or SLA expiration in "which ever happens first" fashion.
*
* @param code Barrier code
* @return Barrier object with barrier code defined
*/
public Barrier runOnBreak(@NonNull BarrierCode code) {
this.mCode = code;
return this;
}
/**
* Defines the optional SLA for the execution. If the condition does not get satisfied till the SLA
* reaches, the defined barrier code will get executed anyways. It takes a parameter,
* shouldPostOnMainThread that dictates on which thread code gets executed. If this method is called
* multiple times on a barrier object before calling {@link Barrier#startSLA()} only the last call
* is honoured. Calling this after {@link Barrier#startSLA()} has no effect.
* Note: It is important to call {@link Barrier#startSLA()} after calling this method that
* triggers the operation of posting the BarrierCode on the required thread. Not calling startSLA()
* will ignore SLA parameter and nothing will happen in relation to SLA.
*
* @param slaInMillis SLA in milli seconds.
* @param shouldPostOnMainThread should the Barrier code be posted on main thread.
* @return Barrier object after capturing SLA.
*/
public Barrier withSLA(long slaInMillis, boolean shouldPostOnMainThread) {
// When SLA is not defined.
if (slaInMillis <= 0) {
throw new IllegalArgumentException("SLA should not be 0 or less than 0");
}
this.mSLA = slaInMillis;
this.mShouldPostOnMainThread = shouldPostOnMainThread;
return this;
}
/**
* This is point from where the SLA counting starts. This call is important if the SLA needs to work.
* This can be called from a different place where the barrier is created. Calling this method multiple times
* has no effect. Only the first call is honoured.
*
* @return Barrier
*/
public Barrier startSLA() {
if (mCode == null) {
throw new IllegalStateException("BarrierCode not defined in the barrier.");
}
if (mSLA == -1) {
throw new IllegalStateException("SLA is not defined and startSLA() called, use withSLA() first.");
}
boolean willStartSLAFromHere = mSlaStarted.pareAndSet(false, true);
if (willStartSLAFromHere) {
if (mShouldPostOnMainThread) {
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.postDelayed(this::tryExecute, mSLA);
} else {
TaskUtilities.runOnBackgroundThreadWithDelay(this::tryExecute, CancellationToken.NONE, mSLA);
}
}
return this;
}
private void tryExecute() {
boolean willExecute = mHasExecuted.pareAndSet(false, true);
if (willExecute) {
mExecutionMechanism.pareAndSet(null, EXECUTED_DUE_TO_SLA);
Log.d(TAG, "Barrier condition did not bee true, started executing due to SLA");
mCode.invoke();
} else {
Log.d(TAG, "Barrier code already executed due to the condition being true. SLA will be ignored.");
}
}
/**
* Barriers can only be broken if we strike/flick them enough no of times. This needs to installed in
* the execution path where the condition needs to be evaluated.
* Once a barrier is broken and {@link BarrierCode} is executed, it will never execute again
* for the same barrier instance.
*/
public void strike() {
if (mCode == null) {
throw new IllegalStateException("Barrier cannot be created without a barrier code, "
+ "Try using runOnBreak() to pass a code for the barrier.");
}
if (mCondition.get() != null && !mHasExecuted.get() && mCondition.get().evaluate()) {
boolean willExecute = mHasExecuted.pareAndSet(false, true);
if (willExecute) {
mExecutionMechanism.pareAndSet(null, EXECUTED_DUE_TO_CONDITION);
mCode.invoke();
Log.d(TAG, "Barrier code started executing due to the condition getting satisfied.");
} else {
Log.d(TAG, "Barrier code already executed due to an smaller SLA");
}
}
}
/**
* Usually the code instance is retained till the barrier instance is in the memory.
* Use clear if the barrier instance has a wider scope and we want to clear the code.
* After calling this method, all invocations of strike will throw IllegalStateException
*/
public void clear() {
mCode = null;
}
/**
* Piece of code that needs to be executed once the barrier is broken due to the
* {@link Condition} getting satisfied or SLA time is expired!
*/
public interface BarrierCode {
void invoke();
}
/**
* Represents the condition that should break the Barrier.
*/
public interface Condition {
/**
* Implementors should override this method to implement their barrier condition.
* {@link Barrier} internally calls evaluate() every time {@link Barrier#strike()} is
* called. Once the condition bees true, if executes the codes represented by
* {@link BarrierCode}
*
* @return true, if the condition is satisfied, false otherwise.
*/
boolean evaluate();
}
}
Isn't setTimeout what you are looking for?
setTimeout(function() {
if (mycondition) {
//do work
}
}, 1000);
Will wait 1000ms and do the statement. If your boolean condition is an event based thing then you could listen for it. What causes the boolean to bee true? It sounds like the timeout is irrelevant and so we just need to check the condition probably every 100ms:
setInterval(function() {
if (mycondition) {
//do work
}
}, 100);
Does that help you?
So the full solution:
var mytimeout, myinterval;
mytimeout = setTimeout(function() {
//do work
clearInterval(myinterval);
}, 5000);
myinterval = setInterval(function() {
if (condition) {
clearTimeout(mytimeout);
//dowork
}
}, 100);
本文标签: javascriptRun code after some time has passed or a condition is metStack Overflow
版权声明:本文标题:javascript - Run code after some time has passed or a condition is met - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741540677a2384302.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论