繁体   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);
    }
}

所以这就是代码,我希望每次我点击我网站上的一个按钮时,它都会等到递归完成然后再开始另一个......

你可以使用async/await吗?

如果可以的话,这将更容易在给定的超时持续时间内通过字符串“暂停”每次迭代(请参阅下面的handleIterateString class function)。

此异步句柄迭代handleIterateString将在每个await关键字处“暂停”,并等到await表达式返回的 promise 已解决。 只有这样它才会继续执行async function。

此外,您可以“暂停” async function 的执行,您可以在其中通过字符串启动新的完整迭代(请参阅下面的async function SetText中的await demo.handleIterateString调用。

通过这种方式,您可以等待整个迭代(即键入行为)完成,然后再减少您的点击队列计数。

如果您的队列中还有点击事件,您可以在此时递归调用SetText


简而言之:使用async/await可以更轻松地控制打字行为的速度,并在执行其他任何操作之前等待您的打字行为完成。

尝试运行下面的代码片段。

 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>

基本思想是创建一个队列,它本质上只是一个数组,您可以在其中推送要在单击后显示的文本。 然后继续处理队列,直到它为空。

代码可能看起来像这样

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

我没有测试过它,所以它可能不起作用,但你应该明白。

暂无
暂无

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

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