Skip to content

Commit eb0109f

Browse files
committed
Avoid race conditions in resuming parts with pre-submit tasks
A question's "resume" signal is only triggered when all of the parts have been submitted, including pre-submit tasks. If a part uses variable replacements, it waits for those parts to be submitted before submitting itself. Maybe Part.submit should return a promise, to make this easier.
1 parent 65238c7 commit eb0109f

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

runtime/scripts/question.js

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -955,27 +955,55 @@ Question.prototype = /** @lends Numbas.Question.prototype */
955955
np.instance.resume();
956956
});
957957
}
958+
959+
const part_submit_promises = {};
960+
q.allParts().forEach(function(p) {
961+
var obj = {};
962+
obj.promise = new Promise(function(resolve, reject) {
963+
obj.resolve = resolve;
964+
});
965+
part_submit_promises[p.path] = obj;
966+
});
958967
/** Submit a given part, setting its `resume` property so it doesn't save to storage.
959968
*
960969
* @param {Numbas.parts.Part} part
961970
*/
971+
const promises_to_wait_for = [];
972+
q.promises_to_wait_for = promises_to_wait_for;
962973
function submit_part(part) {
974+
const {promise, resolve} = part_submit_promises[part.path];
975+
promises_to_wait_for.push(promise);
963976
part.resuming = true;
964-
if(part.answered) {
965-
part.submit();
966-
}
967-
if(part.resume_stagedAnswer!==undefined) {
968-
part.stagedAnswer = part.resume_stagedAnswer;
969-
}
970-
part.resuming = false;
977+
978+
const replacement_promises = part.settings.errorCarriedForwardReplacements.map(vr => part_submit_promises[vr.part].promise);
979+
Promise.all(replacement_promises).then(function() {
980+
if(part.answered) {
981+
part.submit();
982+
}
983+
if(part.resume_stagedAnswer!==undefined) {
984+
part.stagedAnswer = part.resume_stagedAnswer;
985+
}
986+
part.resuming = false;
987+
if(part.waiting_for_pre_submit) {
988+
part.waiting_for_pre_submit.then(function() {
989+
resolve();
990+
});
991+
} else {
992+
resolve();
993+
}
994+
});
971995
}
996+
972997
q.signals.on('ready',function() {
973998
q.parts.forEach(function(part) {
974999
part.steps.forEach(submit_part);
9751000
submit_part(part);
9761001
});
1002+
1003+
Promise.all(promises_to_wait_for).then(function() {
1004+
q.signals.trigger('partsResumed');
1005+
});
9771006
});
978-
q.signals.trigger('partsResumed');
9791007
});
9801008
q.signals.on('partsResumed',function() {
9811009
q.adviceDisplayed = qobj.adviceDisplayed;

0 commit comments

Comments
 (0)