iframe request cancel
Apr 7, 2023Work
问题阐述
公司弹窗是以 iframe 的形式嵌入系统,点击弹窗的确认按钮,会执行回调函数,弹窗也会关闭。
回调函数是异步函数,会触发多个请求,在弹窗关闭后,请求未执行完,这些请求会被浏览器拦截,导致功能异常。

原因分析
参考文章, 这篇文章的高赞回答,解释了为什么会出现这种情况。
这种情况,大多出现在 Chrome 浏览器,Firefox 浏览器不会出现这种情况。而且也取决于网络连接速度。大致有以下几种情况:
- The DOM element that caused the request to be made got deleted (i.e. an IMG is being loaded, but before the load happened, you deleted the IMG node) - 移除了发送请求的 DOM 元素
- You did something that made loading the data unnecessary. (i.e. you started loading a iframe, then changed the src or overwrite the contents) - 改变了 iframe 的 src 或者覆盖了 iframe 的内容
- There are lots of requests going to the same server, and a network problem on earlier requests showed that subsequent requests weren’t going to work (DNS lookup error, earlier (same) request resulted e.g. HTTP 400 error code, etc) - 有许多请求发往同一台服务器,早期请求的网络问题表明后续请求不会起作用
目前公司的情况是第二种,改变了 iframe 的 src,请求未执行完毕就关闭了 iframe。
解决方案
判断异步函数是否执行完毕,执行完毕后才关闭 iframe 弹窗。
cdkc.layerForm({
id: 'form',
title: '新增',
url: 'cdkc_pipe_widgets/kyzgpipe_device/Form.html?areaId=' + areaId,
width: 900,
height: 600,
callBack: function (id) {
return top[id].acceptClick(refreshGirdData);
}
});
cdkc.layerForm({
id: 'form',
title: '新增',
url: 'cdkc_pipe_widgets/kyzgpipe_device/Form.html?areaId=' + areaId,
width: 900,
height: 600,
callBack: function (id) {
return top[id].acceptClick(refreshGirdData);
}
});
// Form.js
acceptClick = async function (cb) {
// ...
await Promise.all([this.save(), this.save1(), this.save2()]);
cb && cb();
};
// Form.js
acceptClick = async function (cb) {
// ...
await Promise.all([this.save(), this.save1(), this.save2()]);
cb && cb();
};
// lr-layer.js
layerForm: function (op) {
var dfop = {
id: null,
title: '系统窗口',
width: 550,
height: 400,
url: 'error',
btn: ['确认', '关闭'],
callBack: false,
maxmin: false,
end: false,
};
$.extend(dfop, op || {});
/*适应窗口大小*/
dfop.width = dfop.width > $(window).width() ? $(window).width() - 10 : dfop.width;
dfop.height = dfop.height > $(window).height() ? $(window).height() - 10 : dfop.height;
var r = top.layer.open({
id: dfop.id,
maxmin: dfop.maxmin,
type: 2, //0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
title: dfop.title,
area: [dfop.width + 'px', dfop.height + 'px'],
btn: dfop.btn,
content: op.url,
skin: dfop.btn == null ? 'lr-layer-nobtn' : 'lr-layer',
success: function (layero, index) {
top['layer_' + dfop.id] = cdkc.iframe($(layero).find('iframe').attr('id'), top.frames);
layero[0].learun_layerid = 'layer_' + dfop.id;
//如果底部有按钮添加-确认并关闭窗口勾选按钮
if (!!dfop.btn && layero.find('.lr-layer-btn-cb').length == 0) {
layero.find('.layui-layer-btn').append('<div class="checkbox lr-layer-btn-cb" myIframeId="layer_' + dfop.id + '" ><label><input checked="checked" type="checkbox" >确认并关闭窗口</label></div>');
}
},
yes: function (index) {
var flag = true;
if (dfop.callBack) {
flag = dfop.callBack('layer_' + dfop.id);
}
if (typeof flag?.then === 'function') {
// we can't use instanceof Promise here because it's from another realm
flag.then(() => cdkc.layerClose('', index))
} else if (flag) {
cdkc.layerClose('', index);
}
},
end: function () {
top['layer_' + dfop.id] = null;
if (!!dfop.end) {
dfop.end();
}
}
});
},
// lr-layer.js
layerForm: function (op) {
var dfop = {
id: null,
title: '系统窗口',
width: 550,
height: 400,
url: 'error',
btn: ['确认', '关闭'],
callBack: false,
maxmin: false,
end: false,
};
$.extend(dfop, op || {});
/*适应窗口大小*/
dfop.width = dfop.width > $(window).width() ? $(window).width() - 10 : dfop.width;
dfop.height = dfop.height > $(window).height() ? $(window).height() - 10 : dfop.height;
var r = top.layer.open({
id: dfop.id,
maxmin: dfop.maxmin,
type: 2, //0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
title: dfop.title,
area: [dfop.width + 'px', dfop.height + 'px'],
btn: dfop.btn,
content: op.url,
skin: dfop.btn == null ? 'lr-layer-nobtn' : 'lr-layer',
success: function (layero, index) {
top['layer_' + dfop.id] = cdkc.iframe($(layero).find('iframe').attr('id'), top.frames);
layero[0].learun_layerid = 'layer_' + dfop.id;
//如果底部有按钮添加-确认并关闭窗口勾选按钮
if (!!dfop.btn && layero.find('.lr-layer-btn-cb').length == 0) {
layero.find('.layui-layer-btn').append('<div class="checkbox lr-layer-btn-cb" myIframeId="layer_' + dfop.id + '" ><label><input checked="checked" type="checkbox" >确认并关闭窗口</label></div>');
}
},
yes: function (index) {
var flag = true;
if (dfop.callBack) {
flag = dfop.callBack('layer_' + dfop.id);
}
if (typeof flag?.then === 'function') {
// we can't use instanceof Promise here because it's from another realm
flag.then(() => cdkc.layerClose('', index))
} else if (flag) {
cdkc.layerClose('', index);
}
},
end: function () {
top['layer_' + dfop.id] = null;
if (!!dfop.end) {
dfop.end();
}
}
});
},
layerForm 函数是弹窗函数,点击确认会执行 yes 函数,yes 函数会执行回调函数,回调函数是 acceptClick 函数,acceptClick 执行完毕后,会关闭弹窗。但是,因为原来没有判断 acceptClick 的返回值是 Promise ,还没有等待这些请求执行完毕,弹窗就关闭了。
所以需要在 lr-layer.js 中,修改 layerForm 函数,判断 acceptClick 的返回值是否是 Promise,参考
// ...
yes: function (index) {
var flag = true;
if (dfop.callBack) {
flag = dfop.callBack('layer_' + dfop.id);
}
// 判断 flag 是否是 Promise
if (typeof flag?.then === 'function') {
// we can't use instanceof Promise here because it's from another realm
flag.then(() => cdkc.layerClose('', index))
} else if (flag) {
cdkc.layerClose('', index);
}
},
// ...
// ...
yes: function (index) {
var flag = true;
if (dfop.callBack) {
flag = dfop.callBack('layer_' + dfop.id);
}
// 判断 flag 是否是 Promise
if (typeof flag?.then === 'function') {
// we can't use instanceof Promise here because it's from another realm
flag.then(() => cdkc.layerClose('', index))
} else if (flag) {
cdkc.layerClose('', index);
}
},
// ...