JavaScript中如何让同步函数暂停等待异步函数执行完成?
同步函数中阻塞等待异步函数执行的实现方案
受JavaScript单线程事件循环机制限制,同步函数内要等待异步函数执行完成,只能通过主动阻塞主线程的方式实现,以下是可行的实现方案:
方案1:Atomics阻塞方案(兼容浏览器+Node.js,推荐)
利用SharedArrayBuffer和Atomics.wait的同步阻塞特性实现等待,不会空跑CPU导致占用过高:
// 通用同步等待Promise工具函数 function syncAwait(promise) { const sab = new SharedArrayBuffer(4); const int32 = new Int32Array(sab); let result = null; let error = null; promise.then(res => result = res) .catch(e => error = e) .finally(() => { Atomics.store(int32, 0, 1); Atomics.notify(int32, 0); }); // 主线程同步阻塞直到收到异步完成通知 Atomics.wait(int32, 0, 0); if (error) throw error; return result; } // 业务代码无需修改draw_a_cat的同步属性 function draw_a_cat(){ draw_cat_body(); // 同步等待异步函数返回结果 const headInfo = syncAwait(get_head_information()); draw_head(headInfo); display_cat_name(); return true; } cat_drawn_yet = draw_a_cat(); console.log("drawn");
使用限制
- 浏览器环境需要页面配置跨域隔离响应头:
Cross-Origin-Opener-Policy: same-origin、Cross-Origin-Embedder-Policy: require-corp,才能使用SharedArrayBuffer - 阻塞期间主线程的所有其他逻辑(UI渲染、交互响应、定时器等)都会暂停
方案2:Node.js专属deasync方案
Node.js环境下可以直接使用第三方模块deasync实现异步转同步,无需额外配置:
const deasync = require('deasync'); function draw_a_cat(){ draw_cat_body(); // 将返回Promise的异步函数转为同步调用 const getHeadInfoSync = deasync((callback) => { get_head_information() .then(res => callback(null, res)) .catch(err => callback(err)); }); const headInfo = getHeadInfoSync(); draw_head(headInfo); display_cat_name(); return true; } cat_drawn_yet = draw_a_cat(); console.log("drawn");
方案3:轮询阻塞方案(兼容性最高,性能最差)
如果前两种方案都不适用,可以用死循环轮询的方式实现阻塞,兼容性最高但CPU占用极高,仅适合极短等待的场景:
function draw_a_cat(){ draw_cat_body(); let headInfo = null; let asyncDone = false; get_head_information().then(res => { headInfo = res; asyncDone = true; }); // 死循环阻塞主线程直到异步执行完成 while(!asyncDone) {} draw_head(headInfo); display_cat_name(); return true; } cat_drawn_yet = draw_a_cat(); console.log("drawn");
注意:所有同步阻塞等待异步的方案都会冻结当前线程的所有执行逻辑,非特殊需求场景优先推荐将
draw_a_cat改为async函数,用标准await语法实现等待逻辑。
内容的提问来源于stack exchange,提问作者Simon Willover
相关产品推荐
相关产品推荐

