【问题标题】:Using async functions to await user input from onclick使用异步函数等待来自 onclick 的用户输入
【发布时间】:2018-12-24 18:11:58
【问题描述】:

我是 async 的新手,也许只是没有深入了解基础知识,但我试图通过调用一个弹出模式并等待用户提交数据的异步函数来等待来自 onclick 的用户输入。在仅找到一两个甚至提到使用异步等待对我的特定任务没有特别帮助的页面事件的来源之后......我想出了这个:

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 的调用。所以显然我错过了这方面的一些关键方面,但我似乎无法从我的来源中提取它。

回调不是一个选项,这里的代码比我能详细说明的要多得多,但上面描述的是我需要执行的基线功能。

【问题讨论】:

  • 对我来说看起来不错。你能提供一个更接近你实际代码的minimal reproducible example吗?
  • 注意你应该使用once而不是on,因为promise只能解析一次,之后它会自动删除处理程序。
  • 我会尽快添加一个,这两个函数都是匿名对象成员函数会有什么不同吗?和/或如果我在var input = 发生之前调用其他两个函数会有所不同吗?想知道我是否也需要等待每个人
  • 问题已解决,我意识到另一部分代码提前调用了 func2 并导致 promise 在错误的情况下返回,从而打乱了流程,以至于当我真正想等待 promise 时...已经解决了。
  • 在这种情况下你可能想删除你的问题

标签: javascript callback async-await event-handling dom-events


【解决方案1】:

我搜索了很多,但什么也没找到,只有这个问题。 而且我想要一些异步的东西而不依赖循环来检查,所以我做了一个不同的解决方案。

主要问题是使用事件侦听器解决 Promise,因此我在 Promise 中创建了 eventListener 函数,并使用箭头函数来获取 Promise 的上下文。然后我解决承诺并在第一次单击后取消 eventListener。

为了让任何监听器都可以重用一个函数,我也为此添加了一个条目。

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 执行的函数中取消它。现在,如果你想做一些依赖于事件顺序的事情,也许使用这个函数可能是一个很好的解决方案。

【讨论】:

    【解决方案2】:

    简单的sn-p

    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>

    【讨论】:

    • 这只允许用户点击按钮两次。如果您希望按钮在单击时始终工作怎么办?请参阅此jsfiddle example 以获得答案。
    猜你喜欢
    • 2021-03-08
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 2023-03-13
    • 2019-06-27
    • 1970-01-01
    相关资源
    最近更新 更多