首先,让我用最简单的术语来说明我想做什么:
我有一系列函数,需要按特定顺序调用;该顺序中的每个函数都有其需要遵循的自己的顺序
我当前的代码,简化:
async function LastModuleFunction() {
await FirstModuleFunction(); // Order:1
await SecondModuleFunction(); // Order: 2
// ...etc...
await EighthModuleFunction(); // Order: 8
setTimeout(GoToNextPage(), 2000); // Order 9
}
function GoToNextPage() {
// some script irrelevant to this question...
window.location.href = 'OtherPage.aspx'; // Order: LAST
}
lastModule.js
async function FirstModuleFunction() {
var obj = { item1: 0, item2: 0, item3: '' }; // you get the idea
FirstModuleDataSaveProcess();
async function FirstModuleDataSaveProcess() {
await FirstModuleDataFunction1(); // Order: 1A
await FirstModuleDataFunction2(); // Order: 1B
// I created a shared function for AJAX
PostDataAjax('AutoSave_ModuleData.asmx/AutoSave_FirstModule', obj, true); // Order: 1C
// The last parameter is just a boolean to tell the function if it should fire a "Saved!"
// message (via Toast) upon success.
}
}
firstModule.js
$('#btnSaveAndFinish').on('click', async function() {
$(this).prop('disabled', true);
$(this).html(waitSpinner); // just a bootstrap "please wait" animation here
LastModuleFunction();
});
moduleNavigation.js
订单必须按照以下模式进行:1A->1B->1C->2A->2B->->;最后
序列中的每个函数都必须等待前一个函数完成
无聊的背景故事&上下文:
我有一个包含多个UserControl模块(ascx)的aspx页面。哪些UC和多少UC取决于用户和一天中的时间(这是一款会议记录应用程序,每天处理4种不同类型的会议)。
每次用户导航到下一个模块(他们在引导程序转盘上进入)时,一系列函数都会准备数据,并通过AJAX将其发送到WebService类(asmx)(请参阅上面的firstModule.js)。我们告诉用户;自动保存";数据随走随用(确实如此)。
在最后一个模块上;下一个";按钮被一个";保存并完成";按钮单击后,此按钮将再次浏览前面的每个模块*,通过每个AJAX函数将所有表单数据保存到数据库,然后location.href
保存到不同的aspx页面。
*用户还能够向前或向后跳到模块,这不会触发";自动保存;因此,该应用程序确保所有数据都已入账
实际发生的情况:
大多数情况下,这是按预期进行的。函数按顺序启动,我给LastModuleFunction()
的2秒缓冲区有足够的时间保存数据,并在重定向前为下一页做好准备。
但有时,它不是。
有两个重要的告诉表明有些事情不正确:
在
FirstModuleFunction()
中,AJAX中调用的WebService更新一个数据行,该数据行保存该会议的核心信息;";MeetingCompleted";该数据行中的列从"N"更改为"Y"。下一页的代码Page_Load
检查这个值是否真的是"Y";如果不是,它将重定向回原始页面,并使用特定的查询字符串告诉我检查失败。因此,在某些情况下,FirstModuleFunction()
似乎没有在重定向触发之前完成。当把
console.log()
调用放在LastModuleFunction()
中的每个函数之前时,每次我测试时,它们都会在控制台上按正确的顺序启动。然而,例如,当我在FirstModuleFunction()
中放置另一个console.log
时,控制台会显示不同的情况:
1st module hit
2nd module hit
3rd module hit
function inside 1st module hit <-- what the...?
4th module hit
[etc.]
控制台
我现在开始认为,在调用async
函数时简单地使用await
并不能完成任务。我不记得我在哪里读到的,它会这么简单,但看起来我要么误解了,要么被误导了,嗯?显然我需要使用承诺。。。这是正确的吗?问题是,我永远无法理解如何让承诺与我需要的这个确切的序列结构一起工作。有人能帮我理解我应该使用什么样的结构吗?
注意:这些函数中的大多数都不返回任何值;它们只是例行公事
修复#1:维护序列(比我想象的更容易)
多亏了Dave的帮助,我才得以解决问题的部分。如果一个函数是await
函数序列的一部分,并且它有它自己的也使用await
的子函数序列,则将await
添加到子函数的ALL,否则下一个";"父";函数不会等待最后一个子函数完成。函数是否最后调用并不重要;如果SecondModuleFunction()
是await
函数,那么PostDataAjax
最终可能会等待它,除非给它一个await
,您的订单可能会被取消。当我应用此修复程序时,控制台每次都给我一个完美的序列(1、1a、1b、1c、2、2a、2b、3、3a等)。
async function FirstModuleFunction() {
var obj = { item1: 0, item2: 0, item3: '' };
await FirstModuleDataSaveProcess(); // <--await added here
async function FirstModuleDataSaveProcess() {
await FirstModuleDataFunction1();
await FirstModuleDataFunction2();
await PostDataAjax('AutoSave_ModuleData.asmx/AutoSave_FirstModule', obj, true); // <--await added here
}
}
换句话说:当有疑问时,等待。
修复#2:等待AJAX(更棘手)
我发现,更大的问题与我提到的那个bug有关——在页面重定向之前,AJAX没有在后台更新服务器端的数据行(AJAX->asmx子例程)。我的所有函数都以完美的顺序启动,,但如果没有正确的重定向等待AJAX对客户端的响应,这些都无关紧要
有时setTimeout
(在我的情况下是2秒)会给它足够的时间;有时情况并非如此。我假设,如果我将await
添加到$.ajax
调用本身,我只是让脚本等待发布,而不是服务器的响应。结果证明我是对的。
在这种情况下,解决方案最终是两个流行的";使脚本等待";解决方案一个当然是await
。另一个是添加回调:
async function PostDataAjax(subdir, dataObj, showSuccess) {
// Toast customization script went here
// ...
// For the callback, we just need SOMETHING. ANYTHING. Don't complicate it.
await PostData(Toast, simpleCallback); // Of course, I added an await. Just in case. ;)
async function PostData(Toast, callback) {
await $.ajax({ // Again, awaiting just in case.
type: 'POST',
url: 'Ajax/' + subdir,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify(dataObj),
success: function (result) {
if (showSuccess === true) {
Toast.fire({
title: 'Saved!',
icon: 'success',
timer: 2000
});
}
callback(); // I'm calling callback both here and in error handling
},
error: function (xhr, status, error) {
Toast.fire({
title: 'Error:',
text: 'Trouble saving data. Please contact the Web Team. (' + error + ')',
icon: 'error'
});
callback();
}
});
}
function simpleCallback() { // As I said: don't complicate it. This is as simple as it gets.
return false;
}
}
这不仅有效,而且允许我继续从GoToNextPage()
调用中删除2秒的setTimeout
。使用console.log()
标记,当我点击";完成并保存";按钮一切都按顺序运行,直到完成后才让页面重定向正是我的目标。
附加说明(2/28/2022):
为了彻底起见,我还应该指出:很明显,在测试了脚本中最有效的内容后,您可以(也应该)在不必要的地方删除await
。要特别是小心在for
或each
循环内部添加等待;当然,它可能只会增加你加载时间的十分之一或二秒,但如果你不需要它,为什么要保留它呢