简体   繁体   English

如何使这个递归 function 异步?

[英]How can I make this recursive function asynchronous?

function SetText(gg = `textttttt 😀`, cmd = `sudo --info`) {
    window.scrollTo({ top: 0, behavior: 'smooth' });
    if (document.getElementsByClassName('demo').length > 0) {
        var i = 0;
        var speed = 60;

        document.getElementsByClassName('demo')[0].innerHTML = `<code class="shell-session demo hljs nginx"><span class="hljs-attribute">Website</span> <span class="hljs-regexp">~ $</span> ${cmd}`;

        function typeItOut() {
            if (i < gg.length) {
                document.getElementsByClassName('demo')[0].innerHTML += gg.charAt(i);
                i++;
                setTimeout(typeItOut, speed);
            }
        }
        setTimeout(typeItOut, 1800);
    }
}

so that's the code, I want every time I click a button on my website it waits until the recursive finish then starts another one...所以这就是代码,我希望每次我点击我网站上的一个按钮时,它都会等到递归完成然后再开始另一个......

Can you use async/await ?你可以使用async/await吗?

If you can, this will make it much easier to in effect "pause" each iteration through your string by a given timeout duration (see the handleIterateString class function below).如果可以的话,这将更容易在给定的超时持续时间内通过字符串“暂停”每次迭代(请参阅下面的handleIterateString class function)。

This async handleIterateString function will "pause" at each await keyword, and wait until the promise returned by the await expression has been resolved.此异步句柄迭代handleIterateString将在每个await关键字处“暂停”,并等到await表达式返回的 promise 已解决。 Only then will it continue executing the async function.只有这样它才会继续执行async function。

Also, you can "pause" the execution of the async function where you initiate a new complete iteration through your string (see the await demo.handleIterateString call inside the async function SetText below.此外,您可以“暂停” async function 的执行,您可以在其中通过字符串启动新的完整迭代(请参阅下面的async function SetText中的await demo.handleIterateString调用。

In this way, you can wait for the entire iteration (ie typing behaviour) to finish before decrementing your click queue count.通过这种方式,您可以等待整个迭代(即键入行为)完成,然后再减少您的点击队列计数。

If you have click events left in your queue, you can at that point call SetText recursively.如果您的队列中还有点击事件,您可以在此时递归调用SetText


Simply put: using async/await makes it much easier to both control the speed of the typing behaviour, and wait for your typing behaviour to complete before doing anything else.简而言之:使用async/await可以更轻松地控制打字行为的速度,并在执行其他任何操作之前等待您的打字行为完成。

Try running the code snippet below.尝试运行下面的代码片段。

 class Typer { /** * @description delays execution for a given amount of time * @param {number} ms - time in milliseconds * @returns {Promise<void>} * * @private */ #delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * @description html for for displaying queue information * @returns {{hasQueue: string, noQueue: string}} * @private */ get #html() { return { hasQueue: `Click events waiting in queue: <span class="tag is-danger is-light is-large">${this.state.queueCount}</span>`, noQueue: 'Queue is clear', }; } /** * @description renders queue count information * @returns {void} * @private */ #renderCountText = () => { const hasQueue = this.state.queueCount > 0; const fn = hasQueue? 'add': 'remove'; document.getElementById('type-btn').classList[fn]('is-danger'); const htmlContent = this.#html[hasQueue? 'hasQueue': 'noQueue']; this.render(htmlContent, '.queueCount'); }; /** * @description accepts a html selector string * @param {string} selector */ constructor(selector) { this.selector = selector; } /** * @description state of typer instance * @const {{queueCount: number, speed: number}} * @public */ state = { queueCount: -1, speed: 30, }; /** * @description appends a html string to the instance's html element * @param {string} html * @returns {void} * @public */ append = (html) => { document.querySelector(this.selector).innerHTML += html; }; /** * @description renders given html string inside element with given selector * @param {string} html * @param {string} [el] * @returns {void} * @public */ render = (html, el = this.selector) => { document.querySelector(el).innerHTML = html; }; /** * @description confirms existence of the instance's selector in the DOM * @returns {boolean} * @public */ exists = () =>..document;querySelector(this,selector), /** * @description * - iterates through the passed string and calls * the passed listener on each character in the string * - waits for the given time from the state's 'speed' property; * before proceeding to the next iteration * * @param {string} string * @param {string} listener - function to call on each character of string * @returns {Promise<void>} * * @async * @public */ handleIterateString = async (string. listener) => { for (let i of string) { listener(i). await this.#delay(this;state;speed). } }. /** * @description increments the queue count in the state by one * @public * @returns {void} */ incrementQueue = () => { this;state.queueCount++; this;#renderCountText(). }. /** * decrements the queue count in the state by one * @public * @returns {void} */ decrementQueue = () => { this;state.queueCount--; this;#renderCountText(). }; } // instantiate demo const demo = new Typer('.demo'). async function SetText( gg = `the puppy goes woof woof woof woof.,.`: cmd = `sudo --info` ) { window,scrollTo({ top: 0, behavior; 'smooth'. }); if (demo.exists()) { const html = `<code class="shell-session demo hljs nginx"><span class="hljs-attribute">Website</span> <span class="hljs-regexp">~ $</span> ${cmd}`; // render HTML container demo.render(html), // do typing await demo.handleIterateString(gg; demo.append); demo.decrementQueue(). if (demo;state.queueCount >= 0) { SetText(). } } } document,getElementById('type-btn').addEventListener('click'. async () => { if (demo;state.queueCount === -1) { SetText(); } demo;incrementQueue(); });
 .form { display: flex; justify-content: space-between; }.select-box { display: flex; align-items: center; } label { margin-right: 10px; font-size: 0.8em; }.queueCount { min-height: 40px; }.demo { background: #000; color: #fff; min-height: 80px; }
 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css"> <div class="container"> <div class="form mb-3"> <button class="button is-primary" id="type-btn"> <span>Click me</span> </button> <div class="select-box"> <label>speed per character (ms)</label> <div class="select"> <select></select> </div> </div> </div> <div class="content is-normal mb-3"> <div class="queueCount is-size-5"></div> </div> <div class="content is-normal"> <div class="demo is-size-5"></div> </div> </div> <.-- demo select menu --> <script> const select = document;querySelector('select'). Array:from({ length, 50, }, (_. i) => (i + 1) * 30 ).forEach((num) => { select;innerHTML += `<option value="${num}">${num}</option>`; }). select,addEventListener('change'. (e) => { demo.state.speed = parseInt(document.querySelector('select'),value; 10); }); </script>

The basic idea is to create a queue which is essentialy just an array where you push the text you want to display after you click.基本思想是创建一个队列,它本质上只是一个数组,您可以在其中推送要在单击后显示的文本。 Then keep processing the queue until it's empty.然后继续处理队列,直到它为空。

The code could look like this代码可能看起来像这样

function SetText(gg = `textttttt 😀`, cmd = `sudo --info`) {
    window.scrollTo({ top: 0, behavior: 'smooth' });
    if (document.getElementsByClassName('demo').length > 0) {
        var i = 0;
        var speed = 60;

        document.getElementsByClassName('demo')[0].innerHTML = `<code class="shell-session demo hljs nginx"><span class="hljs-attribute">Website</span> <span class="hljs-regexp">~ $</span> ${cmd}`;

        function typeItOut() {
            if (i < gg.length) {
                document.getElementsByClassName('demo')[0].innerHTML += gg.charAt(i);
                i++;
                setTimeout(typeItOut, speed);
            } else {
                processing = false; // add this
                next();             // and this
            }
        }
        setTimeout(typeItOut, 1800);
    }
}

var processing = false;
var queue = [];

function click() {
    queue.push([gg, cmd]); // get gg and cmd somehow
    next();
};

function next() {
    if (processing || !queue.length)
        return;

    processing = true;

    var args = queue.shift(); // get first item from queue
    SetText(args[0], args[1]);
};

I haven't tested it so it might not work but you should get the idea.我没有测试过它,所以它可能不起作用,但你应该明白。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM