重构承诺级联与传递的结果

我正在创build一个节点CLI的初稿,以运行docker堆栈和容器。

每个调用都是使用docker命令进行的,具有asynchronous调用和结果。 所以我们决定使用promise。

我们现在的主要问题是当时的着名问题,特别是当我们必须传递价值观,并开始推动纺纱/信息的时候。

这是我们主要的丑陋的方法。 我试图弄清楚这可以被重构为更可读!

 upgradeServiceImage({env, stack, service}) { if (!env || !stack || !service) { throw Error(`You must specify env, stack and service to upgrade a service`); } let payloadSpinner = ora({ text: `Getting upgrade payload. Please wait...` }).start(); this.getServiceUpgradePayload({env, stack, service}) .then((response) => { payloadSpinner.succeed(`Upgrade payload retrieved successfuly for service ${stack} > ${service}`); let upgradeSpinner = ora({ text: `Upgrading the service. Please wait...` }).start(); this.upgradeImage(response) .then((res) => { upgradeSpinner.succeed(`Image upgrade request for service "${service}" was made.`); this.checkUpgrade({env, stack, service}).then((res) => { let finishUpgradeSpinner = ora({ text: `Finishing upgrade of service by deleting old container. Please wait...` }).start(); this.finishUpgrade(response).then(() => { finishUpgradeSpinner.succeed(`Upgrade is now complete!`); }) .catch((err) => { finishUpgradeSpinner.fail(`Finishing upgrade failed. Please see in UI. Err: ${err}`); }) }) .catch((err) => { upgradeSpinner.fail(`${err}`); }) }) .catch((err) => { upgradeSpinner.fail(`Image upgrade failed with error: ${err}`); }); }) .catch((err) => { payloadSpinner.fail(err); }); }; 

它完美的工作,但它几乎不可读和模块化。

我尝试了一些Promise.all的修复,但是我得到了一些问题来正确地恢复我的spinners(加载消息),我无法将对象从一个承诺传递给另一个(例如: response )。

给定这样的代码:

 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); }); 

你可以用一个帮你pipe理微调的帮手来重写它:

 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 }) 

然后,您可以重新使用助手为下一步创build旋钮。

请注意,由withSpinner返回的withSpinner被拒绝,所以如果第一个任务失败,那么第二个任务将不会执行。

这是一个工作演示:

 /* 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();