javascript - 如何使这个递归函数异步?
问题描述
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
下面的类函数)。
此异步handleIterateString
函数将在每个关键字处“暂停” await
,并等待await
表达式返回的承诺被解决。只有这样,它才会继续执行该async
功能。
此外,您可以“暂停”async
函数的执行,您可以在其中通过字符串启动新的完整迭代(请参阅下面的await demo.handleIterateString
调用async function SetText
。
通过这种方式,您可以等待整个迭代(即键入行为)完成,然后再减少您的点击队列计数。
如果您的队列中还有点击事件,您可以在此时SetText
递归调用。
简而言之: usingasync/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>
推荐阅读
- python - 如何在 PyQt5 中为对话框添加最小化和最大化图标?
- python - 如何使用 Ptython OpenCV 从广播视频中检测羽毛球
- html - HTML/CSS:引导幻灯片轮播 - 中心对齐气泡编号
- tensorflow - bazel tensorflow 目标名称误解
- php - PHP数字逻辑
- json - Magento2:graphiql --SyntaxError: Unexpected token < in JSON at position 0
- c# - 如果给定的 if 语句为真或假,如何分配改变的值
- javascript - 强制在 HTTPS 中打开内部链接
- html - Mat-tree 水平滚动。如何添加它?
- css - 通过打字稿更改按钮单击时的svg颜色