Improve code in Recipe.execute with async/await

This commit is contained in:
toby 2017-04-13 12:44:29 -04:00
parent f25bfe96d7
commit 7d4ea26a74

View file

@ -145,10 +145,10 @@ Recipe.prototype.lastOpIndex = function(startIndex) {
* @param {number} [currentStep=0] - The index of the Operation to start executing from * @param {number} [currentStep=0] - The index of the Operation to start executing from
* @returns {number} - The final progress through the recipe * @returns {number} - The final progress through the recipe
*/ */
Recipe.prototype.execute = function(dish, currentStep, state) { Recipe.prototype.execute = async function(dish, currentStep, state) {
var recipe = this; var recipe = this;
var formatErrMsg = function(err, step, op) { let formatErrMsg = function(err, step, op) {
var e = typeof err == "string" ? { message: err } : err; var e = typeof err == "string" ? { message: err } : err;
e.progress = step; e.progress = step;
@ -163,90 +163,40 @@ Recipe.prototype.execute = function(dish, currentStep, state) {
return e; return e;
}; };
// Operations can be asynchronous so we have to return a Promise to a future value. currentStep = currentStep || 0;
return new Promise(function(resolve, reject) {
// Helper function to clean up recursing to the next recipe step.
// It is a closure to avoid having to pass in resolve and reject.
var execRecipe = function(recipe, dish, step, state) {
return recipe.execute(dish, step, state)
.then(function(progress) {
resolve(progress);
})
.catch(function(err) {
// Pass back the error to the previous caller.
// We don't want to handle the error here as the current
// operation did not cause the error, and so it should
// not appear in the error message.
reject(err);
});
};
currentStep = currentStep || 0; if (currentStep >= recipe.opList.length) {
return currentStep;
}
if (currentStep >= recipe.opList.length) { let op = recipe.opList[currentStep],
resolve(currentStep); input = dish.get(op.inputType);
return;
}
var op = recipe.opList[currentStep],
input = dish.get(op.inputType);
try {
if (op.isDisabled()) { if (op.isDisabled()) {
// Skip to next operation // Skip to next operation
var nextStep = currentStep + 1; return recipe.execute(dish, currentStep + 1, state);
execRecipe(recipe, dish, nextStep, state);
} else if (op.isBreakpoint()) { } else if (op.isBreakpoint()) {
// We are at a breakpoint, we shouldn't recurse to the next op. // We are at a breakpoint, we shouldn't recurse to the next op.
resolve(currentStep); return currentStep;
} else { } else if (op.isFlowControl()) {
var operationResult; state = {
progress: currentStep,
// We must try/catch here because op.run can either return dish: dish,
// A) a value opList: recipe.opList,
// B) a promise numJumps: (state && state.numJumps) || 0,
// Promise.resolve -> .catch will handle errors from promises };
// try/catch will handle errors from values state = await op.run(state);
try { let progress = await recipe.execute(state.dish, state.progress + 1);
if (op.isFlowControl()) { return progress;
state = { } else {
progress: currentStep, let output = await op.run(input, op.getIngValues());
dish: dish, dish.set(output, op.outputType);
opList: recipe.opList, return recipe.execute(dish, currentStep + 1, state);
numJumps: (state && state.numJumps) || 0,
};
operationResult = op.run(state);
} else {
operationResult = op.run(input, op.getIngValues());
}
} catch (err) {
reject(formatErrMsg(err, currentStep, op));
return;
}
if (op.isFlowControl()) {
Promise.resolve(operationResult)
.then(function(state) {
return recipe.execute(state.dish, state.progress + 1);
})
.then(function(progress) {
resolve(progress);
})
.catch(function(err) {
reject(formatErrMsg(err, currentStep, op));
});
} else {
Promise.resolve(operationResult)
.then(function(output) {
dish.set(output, op.outputType);
var nextStep = currentStep + 1;
execRecipe(recipe, dish, nextStep, state);
})
.catch(function(err) {
reject(formatErrMsg(err, currentStep, op));
});
}
} }
}); } catch (err) {
throw formatErrMsg(err, currentStep, op);
}
}; };