js的锁以及异步调用相关_ATHER Shu(A.S)的Blog

js的锁以及异步调用相关
sshong 发表于2024年11月30日 10:58:51 更新于2024年11月30日 11:18:18
js通过引入async await promise来解决异步回调地狱问题。
思考下面的两个场景
注:以下示例代码均用typescript实现

let i = 0;
async function handler() {
i++;
console.log(new Date().getTime()/1000, '处理程序开始', i);
await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟异步操作
console.log(new Date().getTime()/1000, '处理程序完成', i);
};

场景一:直接调用handler两次
function call1() {
handler();
handler();
}
call1();

打印如下:
1732934781.962 处理程序开始 1
1732934781.966 处理程序开始 2
1732934783.969 处理程序完成 2
1732934783.97 处理程序完成 2
可以看出,两次handler都同时先后进入(由于js单线程,i会变化),并同时卡在await等待2s。

场景二:await调用handler两次
async function call2() {
await handler();
await handler();
}
call2();

打印如下:
1732934929.563 处理程序开始 1
1732934931.569 处理程序完成 1
1732934931.57 处理程序开始 2
1732934933.571 处理程序完成 2

可以看出,下一次的handler会等上一次执行完毕再进行。

由此可以看出,js如果直接调用函数2次,函数中开头非异步的部分会按调用顺序执行,并同时卡在函数中异步的部分,异步结束后再执行剩余部分。

这很可能造成数据不同步等各种问题。对于要求不管是直接调用还是await调用多次,都要完全保证顺序的地方,需要引入锁机制。

核心就是,函数在进入时立即锁住,即便是直接调用多次,也会确定等待锁释放。
而根据前面的试验结论,可以用一个简单的异步promise来实现这个锁。
let lastlock = Promise.resolve();
async function getlock() {
let unlock = () => {};
let lock = lastlock;
let nlock = new Promise<void>((resolve) => {unlock = resolve;});
lastlock = nlock;
await lock;
return unlock;
}

上面的getlock函数,核心要点
1. lastlock一开始是一个已经resolve的promise,所以await它不会阻塞
2. 每次获取锁,js单线程执行会更改lastlock为一个新的promise,而这个promise的resolve函数会被传出来,也就成了一个unlock函数,这个unlock执行会resolve promise,完成异步。

改造handler函数,用锁
async function handler() {
const unlock = await getlock();
try {
i++;
console.log(new Date().getTime()/1000, '处理程序开始', i);
await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟异步操作
console.log(new Date().getTime()/1000, '处理程序完成', i);
} finally {
unlock();
}
};

连续直接调用handler两次(即call1())
1732935948.492 处理程序开始 1
1732935950.497 处理程序完成 1
1732935950.497 处理程序开始 2
1732935952.499 处理程序完成 2

连续await调用handler两次(即call2()),打印如下
1732936175.165 处理程序开始 1
1732936177.171 处理程序完成 1
1732936177.171 处理程序开始 2
1732936179.173 处理程序完成 2
标签:promise async await 分类:JS&Html5 阅读:2570
评论
暂无评论
添加评论
您的大名,限长10汉字,20英文(*)
电子信箱(*)
您的网站
正文,限长500汉字,1000英文(*)
验证码(*)
分享:

AltStyle によって変換されたページ (->オリジナル) /