如何基于模态框实现可复用的用户决策等待逻辑(避免拆分函数)
如何基于模态框实现可复用的用户决策等待逻辑(避免拆分函数)
我完全懂你的痛点——不想把好好的函数拆成前后两半,就想在函数中间“停一下”等用户点模态框,然后接着往下走,还得让这个模态框逻辑能复用在不同函数里。其实用Promise + async/await就能完美解决,既不用拆分函数,还能把模态框交互封装成可复用的模块,咱们一步步来:
第一步:封装可复用的模态框交互函数
首先,咱们先写一个通用函数,它负责显示模态框,并且返回一个Promise——当用户点击“保存”或“丢弃”按钮时,这个Promise会根据用户的选择resolve对应的结果(比如true代表保存,false代表丢弃)。这样任何需要等待用户决策的地方,只要await这个函数就行。
// 封装可复用的模态框决策函数 function showSaveDiscardModal() { return new Promise((resolve) => { // 显示模态框(这里用Bootstrap模态框为例,自定义模态框逻辑同理) $('#saveDiscardModal').modal('show'); // 按钮点击后的处理逻辑 function handleSave() { $('#saveDiscardModal').modal('hide'); resolve(true); // 告知调用者:用户选择了保存 cleanUpEvents(); // 清理事件,避免重复绑定 } function handleDiscard() { $('#saveDiscardModal').modal('hide'); resolve(false); // 告知调用者:用户选择了丢弃 cleanUpEvents(); } // 绑定按钮事件 $('#btnSave').on('click', handleSave); $('#btnDiscard').on('click', handleDiscard); // 清理事件的辅助函数 function cleanUpEvents() { $('#btnSave').off('click', handleSave); $('#btnDiscard').off('click', handleDiscard); } }); }
第二步:修改原有函数为async函数,用await等待用户决策
接下来,把你的doSomething和doSomethingElse改成async函数,在需要等待用户决策的地方,用await调用上面的showSaveDiscardModal()——这样代码会“暂停”在这里,直到用户做出选择,然后根据返回结果继续执行后续逻辑,完全不用拆分函数!
// 改成async函数,保留完整逻辑 async function doSomething() { let a = new Date(); console.log("it is " + a); // 等待用户在模态框的选择,shouldSave就是用户的决策结果 const shouldSave = await showSaveDiscardModal(); if (!shouldSave) { // 用户选择丢弃 $("#myContainer").replaceWith("<div></div>"); } else { // 用户选择保存 saveChanges(); } } async function doSomethingElse() { let b = 12 * 12; console.log("12 x 12 is " + b); let c = 1 + 1; console.log("1 + 1 is " + c); // 同样等待用户决策 const shouldSave = await showSaveDiscardModal(); if (!shouldSave) { // 用户选择丢弃 $("#myContainer").replaceWith("<div></div>"); } else { // 用户选择保存 alert("Saving Changes Now!"); saveChanges(); } }
第三步:补上模态框的HTML结构
你需要有对应的模态框DOM结构,这里用Bootstrap模态框为例(不用Bootstrap的话,自己写隐藏div+显示/隐藏逻辑也完全可以):
<!-- 通用的保存/丢弃模态框 --> <div class="modal fade" id="saveDiscardModal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">确认操作</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <p>是否要保存当前更改?</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" id="btnDiscard">丢弃更改</button> <button type="button" class="btn btn-primary" id="btnSave">保存更改</button> </div> </div> </div> </div>
第四步:修改事件监听(调用async函数)
最后,调用这些async函数的时候,在事件回调里要加上async/await:
// 按钮点击事件绑定 $("#button1").on("click", async () => { await doSomething(); }); $("#button2").on("click", async () => { await doSomethingElse(); });
为什么这个方案可行?
这里的核心逻辑是用Promise把异步的用户交互(点击按钮)转换成可以用await等待的“同步”代码——async/await就是为了让异步代码看起来像同步代码而设计的,刚好命中你的需求:
- 不用拆分函数:原来的函数逻辑完完整整,只是在需要等待的地方加了一行
await; - 完全复用:所有需要用户决策的地方,都只需要调用
showSaveDiscardModal()就行,模态框逻辑只写一次; - 可读性拉满:代码流程和你最开始写的伪代码几乎一致,一眼就能看懂逻辑走向。
额外注意事项
- 如果不用jQuery/Bootstrap,用原生JS实现模态框的显示/隐藏和事件绑定也完全可以,核心思路还是返回
Promise; - 一定要清理事件绑定(比如示例里的
cleanUpEvents),避免多次点击导致事件重复绑定; - 如果需要自定义模态框的提示文案,可以给
showSaveDiscardModal加个参数,比如showSaveDiscardModal("是否确认丢弃表单内容?"),在函数里修改模态框的提示文本,进一步提升复用性。




