JavaScript 嵌套异步函数调用乱序执行
JavaScript nested async function calls executed out of order
一些上下文:我正在尝试在网站上实现一个功能,该功能会在左侧打开一个面板,然后插入一个表单来编辑网站上当前显示的一些信息。它由点击事件触发,并根据用户在网站上的位置加载不同的表单。为此,我将数据操作(添加或编辑)和数据目标(应编辑的内容)交给函数。
现在可以打开面板并填写信息了,问题是我必须进行几次中间 API 调用(获取部分表格以插入侧面板,加载数据以填写编辑表格with) 并且我在执行此操作时混淆了异步调用
originalButtonElement 和 sidePanelContainer 发生了很多事情,但为了简洁起见,我将截断它。
$(document).on('click', '.side-panel-open', async function() {
console.log('opening panel')
await openSidePanel(originalButtonElement, sidePanelContainer);
console.log('opened panel')
async function openSidePanel(originalButtonElement, sidePanelContainer) {
const sidePanelTarget = originalButtonElement.data('side-panel-target');
const sidePanelAction = originalButtonElement.data('side-panel-action');
const apiReturnPromise = $.ajax({
...
}
});
await apiReturnPromise.done(async function (result) {
...
console.log('loading panel')
await loadSidePanelForm(originalButtonElement, sidePanelTarget, sidePanelAction)
console.log('loaded panel');
return Promise.resolve()
.fail(async function () {
throw new Error();
});
async function loadSidePanelForm(originalButtonElement, target, action) {
switch (target) {
case 'editable-element-1':
console.log('getting form');
await loadSidePanelElementOneForm(originalButtonElement, action);
console.log('got form');
return Promise.resolve()
default:
throw new TypeError(`There is no form for ${target}!`);
}
}
async function loadSidePanelElementOneForm(originalButtonElement, action) {
const apiReturnPromise = $.ajax({
...
};
await apiReturnPromise.done(async function (result) {
if (action === 'add') {
...
await loadSidePanelElementOneAddForm(originalButtonElement, sidePanelContainer);
}
else if (action === 'edit') {
...
console.log('loading edit form');
await loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer);
console.log('loaded edit form')
return Promise.resolve()
}
}
.fail(async function () {
throw new Error();
});
async function loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer) {
const apiReturnPromise = $.ajax({
...
};
await apiReturnPromise.done(async function (result) {
...
return Promise.resolve()
};
.fail(async function () {
throw new Error();
});
}
我了解到,只有 returns 某事,您才能解决承诺。我也明白 ajax 已经 returns 一个承诺,如果我理解正确, .done 函数会解析它,这就是为什么我这样写的原因。然而,日志消息是乱序的,直到 await 'loadSidePanelElementOneForm(originalButtonElement, action);' 它似乎没问题。
opening panel
loading panel
getting form
opened panel
loading edit form
got form
loaded panel
loaded edit form
我已经研究了几个小时但不知所措,尝试了其他几种选择。我究竟做错了什么?我不确定代码的哪一部分未能等待其他部分。
I also understand that ajax already returns a promise and the .done function then resolves it if I understand it correctly
不,.done()
没有“解决它”。它所做的只是安装一个履行处理程序回调。当 ajax 请求完成时,promise 会自行解决,然后将调用 fulfillment/rejection 处理程序。
此外,.done()
doesn't allow chaining of actions like .then()
,它只是 returns 不等待处理程序的原始承诺。不要使用 .done()
/.fail()
,它们基本上已弃用多年。
如果您只是使用 .then()
,您的代码可能会起作用,但实际上您也不应该使用它。您应该只使用 await
语法:
$(document).on('click', '.side-panel-open', async function() {
console.log('opening panel')
await openSidePanel(originalButtonElement, sidePanelContainer);
console.log('opened panel')
});
async function openSidePanel(originalButtonElement, sidePanelContainer) {
const sidePanelTarget = originalButtonElement.data('side-panel-target');
const sidePanelAction = originalButtonElement.data('side-panel-action');
const apiReturnPromise = $.ajax({
…
});
const result = await apiReturnPromise;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
…
console.log('loading panel')
await loadSidePanelForm(originalButtonElement, sidePanelTarget, sidePanelAction)
console.log('loaded panel');
}
async function loadSidePanelForm(originalButtonElement, target, action) {
switch (target) {
case 'editable-element-1':
console.log('getting form');
await loadSidePanelElementOneForm(originalButtonElement, action);
console.log('got form');
return;
default:
throw new TypeError(`There is no form for ${target}!`);
}
}
async function loadSidePanelElementOneForm(originalButtonElement, action) {
const result = await $.ajax({
// ^^^^^^^^^^^^^^^^^^^^
…
});
if (action === 'add') {
…
await loadSidePanelElementOneAddForm(originalButtonElement, sidePanelContainer);
} else if (action === 'edit') {
…
console.log('loading edit form');
await loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer);
console.log('loaded edit form')
}
}
async function loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer) {
const result = await $.ajax({
// ^^^^^^^^^^^^^^^^^^^^
…
});
…
}
一些上下文:我正在尝试在网站上实现一个功能,该功能会在左侧打开一个面板,然后插入一个表单来编辑网站上当前显示的一些信息。它由点击事件触发,并根据用户在网站上的位置加载不同的表单。为此,我将数据操作(添加或编辑)和数据目标(应编辑的内容)交给函数。
现在可以打开面板并填写信息了,问题是我必须进行几次中间 API 调用(获取部分表格以插入侧面板,加载数据以填写编辑表格with) 并且我在执行此操作时混淆了异步调用
originalButtonElement 和 sidePanelContainer 发生了很多事情,但为了简洁起见,我将截断它。
$(document).on('click', '.side-panel-open', async function() {
console.log('opening panel')
await openSidePanel(originalButtonElement, sidePanelContainer);
console.log('opened panel')
async function openSidePanel(originalButtonElement, sidePanelContainer) {
const sidePanelTarget = originalButtonElement.data('side-panel-target');
const sidePanelAction = originalButtonElement.data('side-panel-action');
const apiReturnPromise = $.ajax({
...
}
});
await apiReturnPromise.done(async function (result) {
...
console.log('loading panel')
await loadSidePanelForm(originalButtonElement, sidePanelTarget, sidePanelAction)
console.log('loaded panel');
return Promise.resolve()
.fail(async function () {
throw new Error();
});
async function loadSidePanelForm(originalButtonElement, target, action) {
switch (target) {
case 'editable-element-1':
console.log('getting form');
await loadSidePanelElementOneForm(originalButtonElement, action);
console.log('got form');
return Promise.resolve()
default:
throw new TypeError(`There is no form for ${target}!`);
}
}
async function loadSidePanelElementOneForm(originalButtonElement, action) {
const apiReturnPromise = $.ajax({
...
};
await apiReturnPromise.done(async function (result) {
if (action === 'add') {
...
await loadSidePanelElementOneAddForm(originalButtonElement, sidePanelContainer);
}
else if (action === 'edit') {
...
console.log('loading edit form');
await loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer);
console.log('loaded edit form')
return Promise.resolve()
}
}
.fail(async function () {
throw new Error();
});
async function loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer) {
const apiReturnPromise = $.ajax({
...
};
await apiReturnPromise.done(async function (result) {
...
return Promise.resolve()
};
.fail(async function () {
throw new Error();
});
}
我了解到,只有 returns 某事,您才能解决承诺。我也明白 ajax 已经 returns 一个承诺,如果我理解正确, .done 函数会解析它,这就是为什么我这样写的原因。然而,日志消息是乱序的,直到 await 'loadSidePanelElementOneForm(originalButtonElement, action);' 它似乎没问题。
opening panel
loading panel
getting form
opened panel
loading edit form
got form
loaded panel
loaded edit form
我已经研究了几个小时但不知所措,尝试了其他几种选择。我究竟做错了什么?我不确定代码的哪一部分未能等待其他部分。
I also understand that ajax already returns a promise and the .done function then resolves it if I understand it correctly
不,.done()
没有“解决它”。它所做的只是安装一个履行处理程序回调。当 ajax 请求完成时,promise 会自行解决,然后将调用 fulfillment/rejection 处理程序。
此外,.done()
doesn't allow chaining of actions like .then()
,它只是 returns 不等待处理程序的原始承诺。不要使用 .done()
/.fail()
,它们基本上已弃用多年。
如果您只是使用 .then()
,您的代码可能会起作用,但实际上您也不应该使用它。您应该只使用 await
语法:
$(document).on('click', '.side-panel-open', async function() {
console.log('opening panel')
await openSidePanel(originalButtonElement, sidePanelContainer);
console.log('opened panel')
});
async function openSidePanel(originalButtonElement, sidePanelContainer) {
const sidePanelTarget = originalButtonElement.data('side-panel-target');
const sidePanelAction = originalButtonElement.data('side-panel-action');
const apiReturnPromise = $.ajax({
…
});
const result = await apiReturnPromise;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
…
console.log('loading panel')
await loadSidePanelForm(originalButtonElement, sidePanelTarget, sidePanelAction)
console.log('loaded panel');
}
async function loadSidePanelForm(originalButtonElement, target, action) {
switch (target) {
case 'editable-element-1':
console.log('getting form');
await loadSidePanelElementOneForm(originalButtonElement, action);
console.log('got form');
return;
default:
throw new TypeError(`There is no form for ${target}!`);
}
}
async function loadSidePanelElementOneForm(originalButtonElement, action) {
const result = await $.ajax({
// ^^^^^^^^^^^^^^^^^^^^
…
});
if (action === 'add') {
…
await loadSidePanelElementOneAddForm(originalButtonElement, sidePanelContainer);
} else if (action === 'edit') {
…
console.log('loading edit form');
await loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer);
console.log('loaded edit form')
}
}
async function loadSidePanelElementOneEditForm(originalButtonElement, sidePanelContainer) {
const result = await $.ajax({
// ^^^^^^^^^^^^^^^^^^^^
…
});
…
}