【问题标题】:JavaScript nested async function calls executed out of orderJavaScript 嵌套异步函数调用乱序执行
【发布时间】:2022-02-13 21:02:08
【问题描述】:

一些上下文:我正在尝试在网站上实现一个功能,该功能在左侧打开一个面板,然后插入一个表单来编辑当前在网站上显示的一些信息。它由点击事件触发,并根据用户在网站上的位置加载不同的表单。为此,我将数据操作(添加或编辑)和数据目标(应该编辑的东西)交给函数。

现在打开面板并填写信息正在工作,问题是我必须进行几个中间 API 调用(获取部分表单以插入侧面板,加载数据以填充编辑表单),我正在在我这样做的时候把异步调用搞混了

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();
    });
}

我了解到,只有当它返回一些东西时,你才能解决一个 Promise。我也明白 ajax 已经返回了一个承诺,如果我理解正确, .done 函数就会解决它,因此我这样写它。但是,日志消息是乱序的,直到 await 'loadSidePanelElementOneForm(originalButtonElement, action);'好像没问题。

opening panel
loading panel
getting form
opened panel
loading edit form
got form
loaded panel
loaded edit form

我已经在这方面做了几个小时了,我很茫然,尝试了其他几个选项。我究竟做错了什么?我不确定代码的哪一部分未能等待其他部分。

【问题讨论】:

  • 如果您停止混合回调并且async/await只需 使用async/await,您可能会发现这更容易管理/跨度>

标签: javascript jquery asynchronous async-await promise


【解决方案1】:

我也明白 ajax 已经返回了一个承诺,如果我理解正确, .done 函数就会解决它

不,.done() 不会“解决它”。它所做的只是安装一个履行处理程序回调。当 ajax 请求完成时,promise 会自行解析,然后调用履行/拒绝处理程序。

另外,.done() doesn't allow chaining of actions like .then(),它只是返回不等待处理程序的原始承诺。不要使用.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({
//  ^^^^^^^^^^^^^^^^^^^^
        …
    });
    …
}

【讨论】:

  • 先生,您是圣人。在此期间,我对所有内容进行了一些重构,但应用您建议的更改就可以了!那里有很多关于最佳实践的相互矛盾的信息,我不知道使用 .done() 和 .fail() 已被弃用。
  • 您还在哪里找到使用donefail 的建议?
猜你喜欢
  • 2021-08-11
  • 2023-04-09
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
  • 2019-08-05
  • 2012-10-12
  • 1970-01-01
相关资源
最近更新 更多