使用异步函数等待来自onclick的用户输入

15

我对async还很陌生,也许是我没有掌握基础知识,但我正在尝试通过调用异步函数等待用户从onclick中输入数据,该函数弹出一个模态框并等待用户提交数据。在发现只有一两个来源提到使用async等待页面事件时,并没有对我解决具体任务特别有帮助的情况下... 我想到了这个:

asnyc func1 (){
  var userInput = await async2();

  //do stuff with user input
}
async func2(){
  //build modal content specific to task
  //display modal
  return new Promise(function(resolve,reject){
       $(document).on('click', '#save-input', function(e){
          var input = $('#input-data').val();
          resolve(input);
      });
  });
}

一切似乎都正确调用,并且我获得了用户输入,但是func1从未在调用async2后继续执行。显然,我错过了一些关键方面,但我似乎无法从我的来源中获取它。

回调不是一个选项,在这里的代码远远不止我可以简要说明的内容,但上述描述是我需要执行的基本功能。


1
在我看来没问题。你能提供一个更接近你实际代码的 [mcve] 吗? - Bergi
请注意使用 once 而不是 on,因为 Promise 只能成功一次,并且在此之后应自动删除处理程序。 - Bergi
我会尽快添加一个,如果两个函数都是匿名对象成员函数,会有什么区别吗?或者如果在“var input =”之前调用了另外两个函数,会有什么区别吗?想知道是否需要在每个函数上都加上await。 - Aaron Rosano
1
问题已解决,我意识到代码的另一部分提前调用了func2,导致承诺在错误情况下返回,破坏了流程,以至于当我真正想等待承诺时...它已经被解决了。 - Aaron Rosano
在这种情况下,您可能希望删除您的问题。 - Bergi
2个回答

6

简单片段

const timeout = async ms => new Promise(res => setTimeout(res, ms));
let next = false; // this is to be changed on user input

async function waitUserInput() {
    while (next === false) await timeout(50); // pauses script
    next = false; // reset var
}

使用jQuery的示例用法:

// just change the value of `next` for the script to continue
$('#user-input').click(() => next = true);

async function myFunc() {
    // do stuff before
    await waitUserInput(); // wait until user clicks
    // do stuff after
}

myFunc() // launch function and start waiting for user input

请查看这个可工作的演示。

// this is an async timeout util
const timeout = async ms => new Promise(res => setTimeout(res, ms));

let next = false; // this is to be changed on user input
let n = 1;

async function waitUserInput() {
    while (next === false) await timeout(50); // pause script but avoid browser to freeze ;)
    next = false; // reset var
}

async function myFunc() {
    $('#text').append(`* waiting user input...<br>`)
    await waitUserInput();
    $('#text').append(`* user has clicked ${n++} time(s)<br>`)
    myFunc()
}

$('#user-input').click(() => next = true)

myFunc()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id='user-input' style='padding:15px;color:white; background: tomato; border: 0; border-radius:8px; font-weight: bold'>CLICK ME !</button>
<div id='text'>
</div>


进一步的改进

这个例子可以根据您的需要轻松进行改进。例如,变量next也可以用于存储用户输入的值,并且waitUserInput将返回该值。这将使编写类似以下内容的代码更加容易:

const userResponse = await waitUserInput(); // get user input value

// this is an async timeout util
const timeout = async ms => new Promise(res => setTimeout(res, ms));

let next = false; // this is to be changed on user input

async function waitUserInput() {
    while (next === false) await timeout(50); // pause script but avoid browser to freeze ;)
    const userInputVal = next;
    next = false; // reset var
    return userInputVal;
}

async function myFunc() {
    $('#text').append(`* waiting user input...<br>`)
    const userResponse = await waitUserInput();
    $('#text').append(`* user choice is ${userResponse}<br>`)
    myFunc()
}

$('#user-input').click(function() { next = $('#text-input').val() })

myFunc()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id='text-input' type='text'/> 
<button id='user-input' style='padding:15px;color:white; background: tomato; border: 0; border-radius:8px; font-weight: bold'>SUBMIT !</button>
<div id='text'>
</div>


1
这只允许用户点击按钮两次。如果您希望每次单击按钮时它都能正常工作怎么办?请参见此jsfiddle示例获取答案。 - stomy

4
我搜索了很多,但没有找到关于它的任何内容,只有这个问题。 而且我想要一些异步的解决方案,不依赖循环来检查,所以我采用了不同的解决方案。
主要问题是使用事件监听器解决promisse,所以我在promisse内部创建了eventListener函数,并使用箭头函数获取promisse的上下文。然后我解决了promise并在第一次点击后取消了eventListener。
为了使函数可重用,我也提供了一个入口来处理任何listener。
function waitListener(element, listenerName) {
    return new Promise(function (resolve, reject) {
        var listener = event => {
            element.removeEventListener(listenerName, listener);
            resolve(event);
        };
        element.addEventListener(listenerName, listener);
    });
}

如果您想要将其用于某些内容,它将返回该事件。这使您更容易以有序的方式编写一些代码序列。但是,为了使其工作,它必须在异步函数内部,这意味着该函数将开始并行运行,因此重要的是要知道从哪里开始,以免妨碍您的过程。
在某些异步函数中的简单使用:
var element = document.querySelector("button");
await waitListener(element,"click");

一个完整的样例:

function waitListener(Element, ListenerName) {
    return new Promise(function (resolve, reject) {
        var listener = event => {
            Element.removeEventListener(ListenerName, listener);
            resolve(event);
        };
        Element.addEventListener(ListenerName, listener);
    });
}


async function awaitClicks(){

var element = document.querySelector("button");


await waitListener(element,"click")
// Do thing 1
.then(e=>{
    console.log("e.clientX: "+e.clientX);
});
console.log(1);


await waitListener(element,"click");
// Do thing 2
console.log(2);


await waitListener(element,"click");
// Do thing 3
console.log(3);

    
}
awaitClicks();
<button>click</button>

如果您需要每次单击按钮时发生相同的事情,最好只使用一个addEventListener。根据您要执行的操作,您可以使用addEventListener并在其执行的函数中使用removeEventListener进行取消。现在,如果您想要执行依赖事件顺序的操作,也许使用此函数可以是一个不错的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接