最近在开发中要加一个悲观锁的功能,具体需求是:用户1和用户2不能同时打开一个模型进行编辑,用户1优先进入模型后,要对该模型进行上锁,关闭该模型或刷新页面时要进行解锁,此时在刷新页面时出现了问题。
刷新页面时解锁的接口被浏览器cancel掉了,导致刷新了页面,系统回到了首页,编辑模型页其实已经自动退出了,但解锁接口cancel掉以后无法解锁,导致该模型一直处于占用状态,如图所示:
Chrome浏览器会取消请求的几种场景:
- 触发请求的DOM元素被删除了(比如img元素还没有加载完就被删除了)
- 做了一些不必要的数据加载(比如开始加载iframe后改变其src或重写其内容)
- 大量的请求指向同一个服务器,并且前面请求的网络问题表明后续的请求也走不通(DNS查询错误,前面的请求报400)
由此可得出原因:页面刷新后,页面已经被销毁,但放在mounted中的解锁异步请求还没完成,页面就会主动把未完成的请求取消掉,这时的请求是还没到服务器的。这时满足上述场景的第一条。
解决方式有:
1、利用fetch
Fetch API 提供了一种处理服务器交互的可靠方法,并提供用于不同平台 API 的一致的接口。其中的选项包括 keepalive
,它可确保无论使请求进入的页面是否保持打开状态,请求都会继续执行:
window.addEventListener('beforeunload', {
fetch('/siteAnalytics', {
method: 'POST',
body: "id=123",
keepalive: true
});
}
fetch()
方法的优势在于可以更好地控制发送到服务器的内容。
fetch()
还会返回一个使用 Response
对象解析的 promise。
fetch的具体使用方法如下:
- fetch是传统ajax的升级版本,并不是对ajax的进一步封装,是原生js
- fetch是新的ajax解决方案,fetch会返回Promise
- 更加简单的数据获取方式,功能更强大,更灵活,可以看作是XMLHttpRequest的升级版。
语法结构 :
<body>
<script type="text/javascript">
// POST请求传参
// Fetch会返回Promise 所以我们可以使用then 拿到请求成功的结果
fetch('url', {
method: 'post', // post方法非默认 不可省略
body: 'uname=lisi&pwd=123', // 传递数据,字符串
headers: { // 设置请求头
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function (data) {
// text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,
// 用于获取后台返回的数据 return data.text();
return data.text();
}).then(function (data) {
// 在这个then里面我们能拿到最终的数据
console.log(data)
});
</script>
</body>
2、SendBeacon()
实际上,SendBeacon() 在后台使用 Fetch API,因此它同样具有 64 KB 的载荷限制,以及它还可以确保请求在页面卸载后继续发出。其主要优势在于简单易用。它可让您通过一行代码提交数据:
window.addEventListener('unload', {
navigator.sendBeacon('/siteAnalytics', getStatistics());
}
最终还是推荐fetch,来解决此类情况~