html无法直接封装promise,因它是静态标记语言;promise必须在或模块中用new promise()或async函数定义,富文本编辑器(如quill、tiptap)需在上传钩子中返回promise以统一处理异步流程、错误降级与插入时机。

HTML 编辑器里不能直接“封装 Promise 接口”——Promise 是 JavaScript 运行时概念,必须写在 <script></script> 中或模块文件里;所谓“在编辑器中封装”,实际是指:在富文本编辑器(如 Quill、Tiptap、CKEditor)的上传插件/自定义按钮逻辑中,用 Promise 统一处理文件上传流程,并支持 async/await 消费。
为什么不能直接在 HTML 里写 Promise 封装
HTML 是静态标记语言,不执行 JS 逻辑。<script></script> 标签内才能调用 new Promise() 或 fetch();编辑器本身(比如 Quill 的 image toolbar 按钮)只提供钩子(如 handler),你要在钩子里返回 Promise 才能链式控制加载态、错误提示、插入结果等。
-
new Promise()必须传函数,漏掉或传错类型会立即抛TypeError: Promise resolver undefined - 编辑器调用你的 handler 时,若返回非 Promise(比如直接
return uploadFn(file)但该函数没返回 Promise),后续就无法await或.then() - 富文本场景下,上传常需插入 base64 预览图或服务端返回的 URL,这个“插入时机”必须和 Promise settle 同步,否则出现图片闪烁或位置错乱
Quill 编辑器中 image handler 返回 Promise 的正确写法
Quill 的 handlers.image 默认是同步回调,要让它支持异步,必须显式返回 Promise,并在 resolve 时传入最终 URL(供编辑器插入)。
const quill = new Quill('#editor', {
modules: {
toolbar: {
handlers: {
image: async function() {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
// ✅ 正确:返回一个 Promise,resolve 时传入可插入的 URL
const url = await uploadImage(file);
// Quill 会自动把 url 插入 editor
this.quill.format('image', url);
} catch (err) {
console.error('上传失败:', err);
alert('图片上传失败,请重试');
}
};
input.click();
}
}
}
}
});
// 独立封装的上传函数,返回 Promise
async function uploadImage(file) {
const formData = new FormData();
formData.append('file', file);
const res = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
if (!json.url) throw new Error('响应缺少 url 字段');
return json.url; // ✅ resolve 这个值,Quill 才能插入
}
上传失败时如何降级并保持编辑器可用
文件上传失败不能让编辑器卡住或清空内容。常见错误包括网络中断、后端校验拒绝(如尺寸超限)、FormData 构造失败、CORS 阻断等,这些都应被捕获并 fallback。
- 不要在
catch里直接throw,否则 Quill 会静默吞掉错误;改用alert或触发自定义事件通知上层 - 对大图可先用
FileReader转成dataURL本地预览:reader.readAsDataURL(file),避免用户等待上传完成才看到效果 - 如果后端返回的是相对路径(如
/uploads/abc.png),注意拼接完整 URL,否则图片 404;建议统一在uploadImage内部处理 - 私密模式下
localStorage可能失效,但这里不涉及;真正要兜底的是上传本身——可加内存缓存失败文件列表 + 重试按钮,而非强依赖 Promise 成功
Tiptap 编辑器中 useUploader Hook 的 Promise 封装要点
Tiptap 更现代,推荐用自定义 Extension + useUploader Hook 封装上传逻辑,核心是确保 onUpload 回调返回 Promise。
// 自定义图片节点扩展中的 upload 函数
const uploadFn = async (file) => {
const formData = new FormData();
formData.append('image', file);
const res = await fetch('/api/image', {
method: 'POST',
body: formData,
// ⚠️ 注意:fetch 默认不带 cookie,跨域需加 credentials: 'include'
});
if (!res.ok) {
const text = await res.text();
throw new Error(text || `HTTP ${res.status}`);
}
const data = await res.json();
return data.url; // ✅ 必须返回字符串 URL,Tiptap 才能插入
};
// 在节点 command 中调用
editor.chain().focus().setImage({ src: await uploadFn(file) }).run();
容易被忽略的是:Tiptap 的 setImage 是同步操作,但 src 值必须是有效 URL;如果 uploadFn 返回了 Promise 但没 await,就会插入 [object Promise] —— 这类类型错误不会报 JS 异常,只会让图片不显示。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











