Given code like this:
let payloadSpinner = ora({
text:
}).start();
this.getServiceUpgradePayload({env, stack, service})
.then((response) => {
payloadSpinner.succeed(`Upgrade payload retrieved successfuly for service ${stack} > ${service}`);
// start the second task
})
.catch((err) => {
payloadSpinner.fail(err);
});
you can rewrite it with a helper that manages the spinner for you:
function withSpinner({ task, textOnStart, textOnSuccess, textOnError }) {
const spinner = ora({
text: textOnStart,
})
return task.then(value => {
spinner.succeed(textOnSuccess(value));
return result;
}, reason => {
spinner.fail(reason);
return Promise.reject(reason);
});
}
upgradeServiceImage({env, stack, service}) {
if (!env || !stack || !service) {
throw Error(`You must specify env, stack and service to upgrade a service`);
}
withSpinner({
task: this.getServiceUpgradePayload({env, stack, service}),
textOnStart: `Getting upgrade payload. Please wait...`,
textOnSuccess: result => `Upgrade payload retrieved successfuly for service ${stack} > ${service}`,
textOnError: error => error,
}).then(result => {
// start the second task
})
Then you can re-use the helper to create spinners for next steps.
Note that the promise returned by withSpinner
is rejected, so if the first task fails, the second task won't execute.
Here's a working demo:
/* some scaffolding for the purpose of the example */
let spinnerId = 0;
function ora({ text }) {
let myId = spinnerId++;
console.log("Spinner", myId, "start", text);
return {
succeed(msg) {
console.log("Spinner", myId, "succeed", msg);
},
fail(msg) {
console.log("Spinner", myId, "fail", msg);
}
};
}
function delay(ms, result) {
return new Promise(resolve => setTimeout(() => resolve(result), ms));
}
function delayFail(ms, reason) {
return new Promise((resolve, reject) => setTimeout(() => reject(reason), ms));
}
/* scaffolding ends, actual code begins */
function withSpinner({ task, textOnStart, textOnSuccess, textOnError }) {
const spinner = ora({
text: textOnStart,
})
return task.then(value => {
spinner.succeed(textOnSuccess(value));
return value;
}, reason => {
spinner.fail(reason);
return Promise.reject(reason);
});
}
function upgradeServiceImage() {
return withSpinner({
task: delay(500, "THE UPGRADE PAYLOAD"),
textOnStart: `Getting upgrade payload. Please wait...`,
textOnSuccess: result => `Upgrade payload retrieved successfuly: ${result}`,
textOnError: error => error,
}).then(result => {
return withSpinner({
task: delay(800, "upgradeImage"),
textOnStart: `Upgrading the service. Please wait...`,
textOnSuccess: result => `Image upgrade request for service was made.`,
textOnError: error => error,
});
}).then(result => {
return withSpinner({
task: delayFail(700, "some kind of error"),
textOnStart: `Checking upgrade`,
textOnSuccess: result => `Checking upgrade finished`,
textOnError: error => `CHecking upgrade failed because ${error}`,
});
}).then(result => {
console.log("this won't run anymore because previous step failed");
}).catch(error => {
// additionally log the error if you want
console.error("catch", error);
});
};
upgradeServiceImage();
Update: This is how the same code would look like using async / await. Only the upgradeServiceImage function has been modified. This approach is more 'flat' and readable. It also makes it straightforward to use previous results in consecutive tasks.
/* some scaffolding for the purpose of the example */
let spinnerId = 0;
function ora({ text }) {
let myId = spinnerId++;
console.log("Spinner", myId, "start", text);
return {
succeed(msg) {
console.log("Spinner", myId, "succeed", msg);
},
fail(msg) {
console.log("Spinner", myId, "fail", msg);
}
};
}
function delay(ms, result) {
return new Promise(resolve => setTimeout(() => resolve(result), ms));
}
function delayFail(ms, reason) {
return new Promise((resolve, reject) => setTimeout(() => reject(reason), ms));
}
/* scaffolding ends, actual code begins */
function withSpinner({ task, textOnStart, textOnSuccess, textOnError }) {
const spinner = ora({
text: textOnStart,
})
return task.then(value => {
spinner.succeed(textOnSuccess(value));
return value;
}, reason => {
spinner.fail(reason);
return Promise.reject(reason);
});
}
async function upgradeServiceImage() {
try {
const upgradeResult = await withSpinner({
task: delay(500, "THE UPGRADE PAYLOAD"),
textOnStart: `Getting upgrade payload. Please wait...`,
textOnSuccess: result => `Upgrade payload retrieved successfuly: ${result}`,
textOnError: error => error,
});
const upgradeImageResult = await withSpinner({
task: delay(800, "upgradeImage"),
textOnStart: `Upgrading the service. Please wait...`,
textOnSuccess: result => `Image upgrade request for service was made.`,
textOnError: error => error,
});
const anotherResult = await withSpinner({
task: delayFail(700, "some kind of error"),
textOnStart: `Checking upgrade`,
textOnSuccess: result => `Checking upgrade finished`,
textOnError: error => `CHecking upgrade failed because ${error}`,
});
console.log("this won't run anymore because previous step failed");
} catch (error) {
// additionally log the error if you want
console.error("catch", error);
};
};
upgradeServiceImage();