continueRoutine = False Problem, although it is in Each Frame

URL of experiment: https://run.pavlovia.org/merve73/soa_temporal/html
Code: https://gitlab.pavlovia.org/merve73/soa_temporal/blob/master/html/Soa_original.js

Description of the problem: Hi, I am skipping a routine with continueRoutine = False. It works fine locally but does not skip the routine in online trials. I saw in previous posts it was suggested to put the condition in EachFrame since continueRoutine get true value in each frame. I did put it in the each frame part but nothing changed.

function sound_2RoutineEachFrame(trials) {
  return function () {
    //------Loop for each frame of Routine 'sound_2'-------
    let continueRoutine = true; // until we're told otherwise
    // get current time
    t = sound_2Clock.getTime();
    frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
    // update/draw components on each frame
    if (((automatic === false) && (key_press.keys === null))) {
        continueRoutine = false;
    }
    if (((key_press.keys === "space") && (outcome === 0))) {
        continueRoutine = false;
    }

Do you know any other possible reason for this problem?

Thank you all in advance.

I tried your experiment and didn’t have the error you mentioned. Instead I was getting an unexptected identifier error in line 913. Apart from that, maybe it might worth using ‘’==’’ instead of ‘’===’’ in your if statements. You could have a look at their difference here: https://www.w3schools.com/js/js_comparisons.asp

Sorry, I do not get that identifier error I am a bit confused. But beside that not skipping the routine does not create any error in the code. But when I run the experiment, the routine that should be skipped is not skipped. I tried to change ‘’==", it is again the same.
What I try to do is not generate the sound output if there is no key press. But regardless of whether the key is pressed or not the sound comes. So, actually condition in the each frame part is completely ignored and the sound routine continues in any cases.

Less that the condition is ignored and more that it isn’t being met for some reason. I recommend you add some logging:

unction sound_2RoutineEachFrame(trials) {
  return function () {
    //------Loop for each frame of Routine 'sound_2'-------
    let continueRoutine = true; // until we're told otherwise
    // get current time
    t = sound_2Clock.getTime();
    frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
    // update/draw components on each frame
    console.log(automatic);
    console.log(key_press.keys);
    console.log(outcome);
    if (((automatic === false) && (key_press.keys === null))) {
        console.log("No keys pressed");
        continueRoutine = false;
    }
    if (((key_press.keys === "space") && (outcome === 0))) {
        console.log("Space pressed and outcome is 0");
        continueRoutine = false;
    }

This will tell you if the things that go into your conditional statements have the values you expect, and if not you can either change the conditionals or start tracing those back to figure out why. These messages will appear in your browser’s JS console (in Chrome, you can find this in view->developer)

1 Like

Hi, I tried your suggestion and realized that it actually enters the conditional and changes continueRoutine to false but it checks for continueRoutine at the end of the routine which means that sound has already come. Like this:
‘’’
function sound_2RoutineEachFrame(trials) {
return function () {
//------Loop for each frame of Routine ‘sound_2’-------
let continueRoutine = true; // until we’re told otherwise
// get current time
t = sound_2Clock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
if (((automatic === false) && (key_press.keys === undefined))) {
continueRoutine = false;
}
if (((key_press.keys === “space”) && (outcome === 0))) {
continueRoutine = false;
}

// start/stop sound_1
if (t >= 0.0 && sound_1.status === PsychoJS.Status.NOT_STARTED) {
  // keep track of start time/frame for later
  sound_1.tStart = t;  // (not accounting for frame time here)
  sound_1.frameNStart = frameN;  // exact frame index
  
  psychoJS.window.callOnFlip(function(){ sound_1.play(); });  // screen flip
  sound_1.status = PsychoJS.Status.STARTED;
}
frameRemains = 0.0 + 0.1 - psychoJS.window.monitorFramePeriod * 0.75;  // most of one frame period left
if (sound_1.status === PsychoJS.Status.STARTED && t >= frameRemains) {
  if (0.1 > 0.5) {  sound_1.stop();  // stop the sound (if longer than duration)
    sound_1.status = PsychoJS.Status.FINISHED;
  }
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
  return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}

// check if the Routine should terminate
if (!continueRoutine) {  // a component has requested a forced-end of Routine
  return Scheduler.Event.NEXT;
}

continueRoutine = false;  // reverts to True if at least one component still running
for (const thisComponent of sound_2Components)
  if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
    continueRoutine = true;
    break;
  }

‘’’

So, I actually solved my problem by putting check if (!continueRoutine) part right after my conditional but I wonder why it would happen. Do you have any idea? Since this was the automatic translation of builder maybe there is a logic behind its being put like this that I do not see.

It’s a quirk of tripping this on the very first frame before anything has happened. PsychoPy doesn’t really assume you would create a trial that displays stimuli immediately but then cancel it on the first frame (which is basically what you’re doing here), so the way the builder code works is “plug code component in at start of frame, then check if it’s time to play these stimuli and change their status if needed, then check if it’s time to end”, which makes sense most of the time but not here.

The other solution that would likely have worked is if you set the start time of the sound stimulus to 100ms (or even 50ms) after the start of trial, because then it wouldn’t try to start the sound on the same frame that it realizes it should cancel the trial. That might be better long-term because if you recompile your code from the builder, the change you’re describing might get over-written by the auto-generated code.

1 Like

Hi all,

I am having the same problem (using Psychopy 2020.2.4 on Macbook Pro Mojave) in my Pavlovia Experiment. I have a code component with continueRoutine = True or False depending on the rep of the outer loop (LRblock). This works perfectly well in Psychopy Builder but it does not work on Pavlovia. In the builder version, I had the code component in the Begin Routine tab but I have moved it to the Each Frame tab as that seems to work better when working with Pavlovia.

This is what I am putting in my simple code component (see the pic of it below too):

if LRblock.thisN < 2:
    continueRoutine = True
elif LRblock.thisN == 2:
    continueRoutine = False

Here are some visuals:


So basically, the LRpause routine should only continue if the LRblock is on the first (==0) or second (==1) loop. It should NOT continue if it is on the third (==2) loop. As of now, it is continuing even when LRblock.thisN == 2 in the pavlovia verison.

I have tried replace .thisN with .thisTrialN too and that did not work.

I also made sure that the text that is part of the LRpause routine does not start until after 1sec as suggested above but this did not work either.

I hope someone can help! My experiment is in pilot private mode right now but I am happy to move it to public mode if that would help!

Thanks!!

Hey, have you tried printing this to the console to check that it is meeting your conditional?

console.log(LRblock.thisN)

Not yet. Should I do that after running it locally? Or it does not matter?

Add it to your JS script and run it online. You can view the console printout by opening developer tools (see JS crib sheet)

If it is running fine locally but not online it means the issue is on that side of things so best to try there :blush:

Unfortunately, .thisN doesn’t work in versions 2020.2 to 2020.24. I’ve been switching my code to using an index variable Idx which I increment at the end of each loop.

Where do you initialize your index variable? How do you call it? I am also looking into what Becca said…

Put Idx = 0 in a Begin Experiment tab

Put Idx +=1 at the end of an End Routine tab in the final routine of the loop

Use Idx instead of trials.thisN

OK yes so by printing thisN (using the method I mentioned trying) you would have then seen in the console that thisN wasn’t increasing as you would expect (which would show you it is the issue wakefield mentions) - So essentially, you can skip my step and, as wakefield says, you make yourself a numeric variable that increases by 1 on each trial (your making yourself a version of thisN to use :slight_smile: )

1 Like

Thanks so much for your help. I tried doing what wakecarter said but it still did not work. The LRpause routine is still continuing after the third iteration of LRblock… I may have misunderstood (high probability :slight_smile:). This is what I have:

  1. Idx = 0 in a begin experiment tab:

  2. Indx +=1 at the end of an End routine tab in the final routine of the loop:

  3. The code component in my LRpause routine uses Idx instead of LRblock.thisN

OK so on your last component there add the:

console.log(idx)

to line 1 - then when you run online you will be able to check if your Idx value is as you expect in the dev tools

if it doesn’t ===2 at the start of your routine it will be easier to suss out

ok thanks @Becca I will run again and get back to you! Just to confirm, add console.log(idx) above:
if Idx <2: on line 1?

yes :slight_smile: so you print the value before you enter the if statement where you think the routine should be ending

1 Like

When I tried to run, I got a reference error right before the first iteration of LRblock ended :frowning: saying:

Idx is not defined

It seems that it got confused by the console.log(idx) line maybe?

can you see a line number where the error occurs when you open developer tools? that will tell you the line where the error is