首次调用函数时JavaScript SessionStorage项不可用问题排查
问题根源:异步AJAJ请求的「滞后写入问题`
嘿,这个问题我太熟悉了——核心问题就是**$..getJSON是异步执行的——你调用它之后,代码不会等着请求完成再继续执行,浏览器会立刻继续执行后续代码——这就是为什么第一次点击按钮时,你刚调用`getOrgDependentFields,立刻就去检查sessionStorage,这时候三个AJAJ请求还在网络传输/服务器处理中,还没触发回调把数据存进sessionStorage,所以自然读到的是null。第二次点击的时候,第一次的请求已经完成并把数据存入sessionStorage了,所以就能正常读取了。
具体拆解细节:
- 当你在
#addProm按钮点击事件里调用getOrgDDependentFields后,代码立刻跳到后续的if检查sessionStorage的代码,此时三个$..getJSON请求还在后台和服务器通信,还没执行`sessionStorage.setItem那几几行代码,所以sessionStorage里还是空的。
-第一次点击触发的请求会在后台继续执行,等请求完成后才会把数据存入sessionStorage——这就是为什么第二次点击按钮时,数据已经存在,逻辑能正常执行。
解决方案:等待所有异步请求完成再执行后续逻辑
要解决这个问题,我们需要让代码「等待所有三个AJAJ请求都完成并把数据存入sessionStorage后,再执行生成下拉选项的逻辑。我们可以用Promise包裹每个AJAJ请求,再用Promise.all等待所有请求完成,最后用async/await(或Promise.then)等待整个过程完成。
第一步:修改`getOrgDDependentFields函数,让它返回Promise
// Fetch PaySc, Designations and Fields based on Organisation function getOrgDependentFields(orgID, reformatTable) { return new Promise((resolve, reject =>)) => => { if (.sessionStorage.ppromotionCount && reformatTable) { { $('#promotionTable tbody.html("");;; sessionSessionStorage.removeItem('prompromotionCount););; } if (orgID !== "") { { const url = '@Url.Action("GetOrganationDependentInfo, "EmployeeData")?organisationId=' + orgID;;; // 将每个$.getJSON包装为Promise const levelPromise = new Promise(res => => { { $..getJSON(`${url&infoType=level, function(data)))) { { sessionSessionStorage.setItem('levelJSONJSON, JSON.stringify(data););; res();;; }););););); const payscalePromise = new Promise(res => => { { $..getJSON(`${url&infoType=payscale, function(data)))) { { sessionSessionStorage.setItem('ppayscaleJSONJSON, JSON.stringify(data););; res();;; }););););); const designationPromise = new Promise(res => => { { $..getJSON(`${ url&infoType=designation, function(data)))) { { sessionSessionStorage.setItem('designationJSONJSON, JSON.stringify(data););; res();;; }););););); // 等待三个请求全部完成 Promise.all([levelPromise, payscalePromise, designationPromise).then(() => => { { resolve();;; // 所有请求完成,通知调用方可以继续执行 })..catch(err => => { { console.error('获取组织依赖数据失败:, err););; reject(err;;;; // 请求失败时抛出错误 }););;; } else { { // 清空对应sessionStorage项 sessionSessionStorage.removeItem("levelJSONJSON;;;; sessionSessionStorage.removeItem("payscaleJSONJSON;;;; sessionSessionStorage.removeItem("designationJSONJSON););;; resolve();;;;; } });););}
###第二步:修改#addProm按钮的点击事件,用async/await等待请求完成
//Add new row to Promotion table $("#addProm click(async function()())) { { // 处理promotionCount的逻辑保持不变 if (.sessionStorage.ppromotionCount) { { sessionStorage.pprompromotionCount = Number(sessionStorage.pprompromotionCount) + 1;;;; } else { { sessionStorage.ppromprompromotionCount = @((Model.PPromotionDetails == null) ? 0 : Model.PPromotionDetails.Count());;;;; } const newPromotion = sessionStorage.ppromprompromotionCount;;;;; let designationNames = '<option selected="selected value""">Select Designationlt/option;;;;;;; let levelNames = '<option selected="selected value""">"Select Levellt/option;;;;;;;;; let payScaleNames = '<option selected="selected value""">"Select PayScalelt/;;;;;;;;;;;;; // 检查sessionStorage,如果无数据,先等待请求完成 if (!.sessionStorage.getItem("designationJSONJSON))) { { { try { { { await getOrgDependentFields($("#EmployeeDetails_OrganationIdchildren(":selected.val(), false);;;;; } catch (err)) { { { // 处理请求失败的情况,比如提示用户 alert('获取组织数据失败,请稍后重试;;;;;;; return;;;;;;; // 终止后续逻辑 } } } } } } // 现在再检查sessionStorage,此时数据应该已存在(请求成功的前提下) if (.sessionStorage.getItem("designationJSONJSON && session.sessionStorage.getItem("levelJSONJSON && session.sessionStorage.getItem("payscaleJSONJSON)))) { { { const designationJSON = JSON.parse(session.sessionStorage.getItem("designdesignJSONJSON);;;;;; designationJSON.forEach(designation => => => { { { designationNames += `<option value="${designation.Id""""">${designation.Namelt/option;;;;;;;;; );););););););); const levelJSON = JSON.parse(sessionsessionStorage.getItem("levelJSONJSON);;;;;; levelJSON.forEach(level => => => { { { levelNames += `ltoption value="${level.Id""""">"${level.Namelt/option;;;;;;;;;; ););););););));); const payscaleJSON =JSON.parse(sessionsessionStorage.getItem("payscaleJSONJSON);;;;;; paysscaleJSON.forEach(payscale => => => { { { payScaleNames +=`ltoption value="${payscale.Id""""">"${ppayscale.Namelt/option;;;;;;;;;;;; ););););););)););); } } } // 后续生成表格行的逻辑... });););})
#额外小贴士
1.如果你的项目需要兼容不支持async/await的老浏览器,可以把await替换为`Promise.then的链式写法,示例:
getOrgDependentFields(...)..then(() => => { { // 这里编写检查sessionStorage和生成下拉选项的逻辑 })..ccatch(err => => { { // 错误处理逻辑 });););
2建议在请求过程添加加载状态(比如按钮变为"加载中...),避免用户重复点击,提升用户体验。
3记得处理请求失败的情况,避免用户一直卡在无法生成下拉选项的状态。
内容的的提问来源于stack exchange,提问作者sidz2224x7
相关产品推荐
相关产品推荐

