在提交表单之前等待异步调用完成

Posted

技术标签:

【中文标题】在提交表单之前等待异步调用完成【英文标题】:Waiting for an async call to finish before submitting a form 【发布时间】:2018-10-18 13:08:30 【问题描述】:

这个有点棘手。

这是我的 jQuery 代码:

/**
 * Updating websites
 */
$('.website').on('blur', function () 
    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    fetch(`/api/edit/$websiteId&$website`).then(() => 

    );
)

这是我的 html

<form method="post">
    <% for(let i = 0; i < websites.length; i++) let website = websites[i]; %>
    <fieldset id="site<%= i %>">
        <p style="color: <% if(errorMsgs[i] === 'Wrong website address') %> red;<%  else  %> green;<%  %>
                padding-bottom: 0;
                padding-top: 10px;
                margin-bottom: 0;"
           class="">
            <%= errorMsgs[i] %>
        </p>
        <div class="">
            <input class="website"
                   name="website<%= i %>"
                   id="<%= i %>"
                   value="<%= website %>"
                   type="text"/>
            <a href="/websites/edit/<%= i %>"
               class="btn btn-info hide">
                Edit
            </a>
            <a href="/websites/delete/<%= i %>"
               data-id="<%= i %>"
               class="btn btn-danger confirmation removeBtn">
                Remove
            </a>
        </div>
    </fieldset>
    <%  %>
    <button type="submit"
            class="btn btn-primary col-sm-offset-2"
            value="Generate a report"
            name="generateReport"
            data-toggle="modal"
            data-target="#exampleModal">
        Generate a report
    </button>
    <!-- Save button created using javascript. In case JS is disabled -->
</form>

这是我的 API

// GET: api/edit/_id - save updates
router.get('/edit/:_id&:url', (req, res, next) => 
    Account.findById(req.user._id, (err, acc) => 
        if (err) 
            console.log(err);
         else 
            acc.websites.set(req.params._id, req.params.url);
            acc.save((err, webs) => 
                if (err) 
                    console.log(err);
                 else 
                    console.log('all good');
                    // res.redirect('/reports');
                    res.json(webs);
                
            );
        
    );
);

它是如何工作的:用户将值输入到输入字段中,模糊时,我对 API 进行 AJAX 调用,值保存在数据库中。这一切都有效。

但是有一个问题。如果用户点击提交按钮,则获取没有时间运行。这是有道理的。

但我需要等到值更新后才提交。我正在考虑这样做,在提交按钮上添加一个preventDefault,然后在保存后,删除preventDefault

但我不知道该怎么做,不管这是个好主意还是有意义。

回顾一下:我需要等到 AJAX 调用完成后才提交表单。

这个&lt;% smth %&gt; 只是EJS 模板系统。不应该影响它的运作方式。

【问题讨论】:

你不能在按钮上设置 disabled 属性并在 .then 中得到响应后将其删除吗? 【参考方案1】:

preventDefault 制作一个虚假的提交按钮并用display:none 隐藏你的真实提交按钮怎么样?然后,您可以在 AJAX 成功后在隐藏按钮上调用 click() 函数。

【讨论】:

该死的鬼鬼祟祟。我试试看 您不需要隐藏按钮。表单有一个可以调用的submit 方法。【参考方案2】:

您可以将按钮设置为禁用状态,为其添加侦听器,然后在 .then() 函数期间引发事件。

$('.website').on('blur', function () 
    var elem = document.getElementById('submitButtonId');

    // create the event
    var isFinishedEvent = new Event('fetched');

    elem.addEventListener('fetched', function() 
        // logic to enable your button
    

    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    fetch(`/api/edit/$websiteId&$website`).then(() => 
       // Fire the event
       elem.dispatchEvent(isFinishedEvent);
  );
)

【讨论】:

我想我会结合你和 p0rter 的答案。给我一秒钟【参考方案3】:

看起来fetch() 返回了一个承诺。将这些承诺保存在共享变量中。在您的提交按钮代码中,使用Promise.all() 等待所有承诺解决。比如:

const fetchPromises = [];
$('.website').on('blur', function () 
    var websiteId = this.id;
    console.log(websiteId);
    var website = this.value;
    console.log(website);
    var fetchPromise = fetch(`/api/edit/$websiteId&$website`).then(() => 

    );
    fetchPromises.push(fetchPromise);
);
$('form').on('submit', function (e) 
    e.preventDefault();
    Promise.all(fetchPromises).then(() => this.submit());
);

您可能想要添加一些错误处理。如果您的任何fetch() 调用失败,这将阻止提交表单。理想情况下,失败的 AJAX 调用会导致向用户发送一些消息,一旦解除,就会从 fetchPromises 数组中删除承诺,并且在解除之前,提交按钮应该被禁用(实际上是可见的)。

【讨论】:

如果我理解正确,这只是将承诺添加到数组中,并在提交事件上运行它们。对吗? fetch() 调用没有任何变化。底层 AJAX 请求与您当前的代码同时发出。我们正在存储返回的承诺以防在承诺解决之前单击提交按钮。如果 AJAX 请求在表单提交之前完成,Promise.all() 将立即解析,并且表单提交将立即完成。 感谢您的解释。现在说得通了!

以上是关于在提交表单之前等待异步调用完成的主要内容,如果未能解决你的问题,请参考以下文章

如何在表单提交之前将多个图像异步添加到 django 表单

jquery ajax 表单提交被浏览器拦截解决办法

提交常规表单之前的 AJAX 请求

Parsley 验证不等待远程验证 AsyncValidate 完成

Coldfusion,5个表单提交到iframe

同步与异步的概念