0

I am using js and jsPsych to code an experiment.

At the end of the task I want to save the data to Firebase Firestore and then redirect to a location. However, currently no data is being saved when I include the window.location.replace. It works fine without the redirection. But I need both. Any advice would be much appreciated.

jsPsych.init({
timeline: timeline,
preload_images: [
  on_finish: function() {(saveData(jsPsych.data.get().json()));
    (window.location.replace("https://url.com"));
  },
      });

  function saveData(data){
    console.log("trying to save");
    const db = firebase.firestore();
    var data = JSON.parse(data);
    var namedData = {};
    data.forEach(function(q) {
 
    //console.log(q.internal_node_id)

    if(q.hasOwnProperty("responses"))
    {
      q.responses = JSON.parse(q.responses);
    }
    namedData[q.internal_node_id] = q;

  })
    //db.collection("user").doc(subject_id).set(namedData)
    db.collection("user").doc(subject_id).set(namedData)
      .then(function() {
        console.log("data saved")
      })
  }

Many thanks!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Lara
  • 5
  • 5
  • Could it be that you're navigating away from the page before the Promise `set(namedData)` is Resolved? To test this hypothesis, you could move your `window.location.replace` into the body of that last `then` function and see if the DB write works. – Noah Dec 31 '20 at 15:27
  • Your redirection happens before saving the data. The call to `saveData` and `window.location.replace` is synchronous. Nevertheless, as you are using a Promise `db....then`, `saveData` itself is asynchronous. Therefore, you redirect without awaiting for the insertion into your database to complete. So yes, most probably the data is being saved, just not right when you redirect but a little bit later. Check out JavaScript's async/await for a possible solution or place the redirect into the `then` callback. – Simon van Endern Dec 31 '20 at 16:02

1 Answers1

0

A folks have commented: you are currently redirecting before the data has been saved to the database. To prevent this, you have to wait until the data is saved, for which you can use the then() that you're currently using to log.

So:

  on_finish: function() {
    saveData(jsPsych.data.get().json());
  },
});

  function saveData(data){
    console.log("trying to save");
    const db = firebase.firestore();
    var data = JSON.parse(data);
    var namedData = {};
    data.forEach(function(q) {
 
      if(q.hasOwnProperty("responses")) {
        q.responses = JSON.parse(q.responses);
      }
      namedData[q.internal_node_id] = q;

    })
    db.collection("user").doc(subject_id).set(namedData)
      .then(function() {
        console.log("data saved");
        window.location.replace("https://url.com")
      })
  }

Of if you'd prefer you can return the promise from saveData and use it in the caller:

  on_finish: function() {
    saveData(jsPsych.data.get().json()).then(function() {
      window.location.replace("https://url.com");
    });
  },
});

  function saveData(data){
    console.log("trying to save");
    const db = firebase.firestore();
    var data = JSON.parse(data);
    var namedData = {};
    data.forEach(function(q) {
 
      if(q.hasOwnProperty("responses")) {
        q.responses = JSON.parse(q.responses);
      }
      namedData[q.internal_node_id] = q;

    })
    return db.collection("user").doc(subject_id).set(namedData);
  }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks @Frank van Puffelen! This was very helpful! More generally is there a way to have two separate functions in on_finish:e.g., ``on_finish: function(){$("#jspsych-content").css({'border-color': "white",'border-style':'solid','border-width':'40px'}) and function(data){data.Trial = trial}`` in one on_finish? I am struggling to wrap my head around this part of js. – Lara Jan 04 '21 at 17:43
  • You can combine the code into a single `on_finish` like this: `on_finish: function(){ $("#jspsych-content").css({'border-color': "white",'border-style':'solid','border-width':'40px'; data.Trial = trial; }`. That way both will be executed when `on_finish` gets called. – Frank van Puffelen Jan 04 '21 at 18:49