如何将来自不同函数的承诺链接未知次数(迭代数组)
How to chain promises from different functions an unknown number of times (iterating an array)
我正在从头开始构建一个练习项目。这是一个香草 javascript 插件,可让您创建“动画打字效果”。
到目前为止的演示代码笔:https://codepen.io/juan-sebasti-n-bonnett/pen/oNoVGad
const TypingEffect = {
options: {
parallel: false,
showCursor: true,
removeOtherCursors: true,
removeCursorAfter: 0,
cursorColor: '#000',
cursorBG: '#000',
cursorWeight: '0px',
cursorInterval: 400,
typingSpeed: 80,
cursorChar: '|',
eraseSpeed: 50,
showcaseEraseDelay: 3000,
},
type: function(_elem, _str) {
let elem;
if (typeof(_elem) != 'string' && typeof(_elem) != 'object') return;
if (typeof(_elem) == 'string') {
console.log('we have to query a selector');
elem = document.querySelector(_elem);
console.log(elem);
}
if (this.options.showCursor) elem = this.cursor(elem);
let typingSpeed = parseInt(this.options.typingSpeed);
let str = _str || elem.innerHTML;
let strArray = str.split('');
let currentString = '';
let i = 0;
return new Promise((resolve, reject) => {
let typing = setInterval(() => {
if (i < strArray.length) {
currentString = currentString + strArray[i];
elem.innerHTML = currentString;
i++;
} else {
clearInterval(typing);
resolve(elem);
}
}, typingSpeed);
});
},
typeMultiple: function(elements) {
let promise = Promise.resolve();
elements.forEach(element => {
promise = promise.then(() => {
return this.type(element);
});
});
promise.then(() => {
if (parseInt(this.options.removeCursorAfter) > 0) {
setTimeout(() => {
this.removeCursor();
}, this.options.removeCursorAfter);
}
});
return promise;
},
typeSelector: function(selector) {
let elements = document.querySelectorAll(selector);
if (this.options.parallel) {
elements.forEach(element => {
this.type(element);
});;
} else {
return this.typeMultiple(elements);
}
},
erase: function(elem) {
if (typeof(elem) != 'string' && typeof(elem) != 'object') return;
if (typeof(elem) == 'string') {
elem = document.querySelector(elem);
}
let str = elem.innerHTML;
return new Promise((resolve, reject) => {
let erasing = setInterval(() => {
if (str.length > 0) {
str = str.slice(0, -1);
elem.innerHTML = str;
} else {
clearInterval(erasing);
resolve(elem);
}
}, parseInt(this.options.eraseSpeed));
});
},
cursor: function(elem) {
if (this.options.removeOtherCursors) this.removeCursor();
let string = elem.innerHTML;
let cursorInterval = parseInt(this.options.cursorInterval);
let cursorVisible = true;
const cursorNode = document.createElement('span');
const cursor = document.createTextNode(this.options.cursorChar);
const stringNode = document.createElement('span');
const stringContent = document.createTextNode(string);
elem.innerHTML = '';
stringNode.appendChild(stringContent);
elem.appendChild(stringNode);
stringNode.id = 'typing-string-content';
cursorNode.appendChild(cursor);
elem.appendChild(cursorNode);
cursorNode.id = 'typing-cursor';
cursorNode.style.paddingLeft = this.options.cursorWeight;
cursorNode.style.backgroundColor = this.options.cursorBG;
cursorNode.style.color = this.options.cursorColor;
setInterval(() => {
if (cursorVisible) {
cursorNode.style.opacity = 0;
cursorVisible = false;
} else {
cursorNode.style.opacity = 1;
cursorVisible = true;
}
}, cursorInterval);
return stringNode;
},
removeCursor: function(_cursor) {
let cursor = _cursor || document.getElementById('typing-cursor');
if (cursor) cursor.remove();
},
init: function(_options) {
if (_options) Object.assign(this.options, _options);
},
}
/*
Typing Effect usage
*/
let newOptions = {
typingSpeed: 120,
eraseSpeed: 120,
cursorColor: 'rgb(14, 231, 32)',
}
TypingEffect.init(newOptions);
let string = `Hello! I'm using the type() function`;
let done = TypingEffect.type('#type-here', string);
done.then((elem) => {
if (elem) {
let notice = document.getElementById('notice');
notice.innerHTML = `Done Typing! But I will be erased after 6 seconds`;
setTimeout(() => {
let doneErasing = TypingEffect.erase(elem);
doneErasing.then((elem) => {
notice.innerHTML = `Done erasing!`;
});
}, 6000);
}
});
* {
box-sizing: border-box;
}
body {
background-color: #000;
}
.container {
width: 100%;
/*display: flex;
align-items: center;
justify-content: center;*/
}
.typing-effect {
font-family: 'Consolas';
color: rgb(14, 231, 32);
font-size: 22px;
}
#notice {
color: white;
font-size: 22px;
font-family: sans-serif;
padding-top: 54px;
}
<div class="container">
<p id="type-here" class="typing-effect"></p>
</div>
<div id="notice"></div>
我已经有了允许我在 HTML 元素中键入字符串的机制和允许我删除它的另一个机制。但我现在的目标是能够创建第三个函数,它接收一个句子数组并开始输入一个句子,然后稍等片刻,擦除它然后输入另一个句子直到数组结束,然后从索引 0.
现在的代码使用 Promise,逻辑如下:
function type(selector, string) {
// returns a promise that resolves
// when we're done typing the string
// and returns the element that contains the text (the selector)
}
function erase(selector) {
// returns a promise that resolves when
// we're done erasing the whole node's content
// and returns the element that is now empty (the selector)
}
现在,我对 promises 这个概念还是陌生的,虽然当用 API 解释它们时它们很容易理解,因此很难链式操作它们,主要是当你需要一些东西的时候像这样:
function typeMultipleSentences(selector, arrayOfSentences) {
// LOOP:
// types the first sentence of the array
// when its done typing, waits for a couple seconds and then erases it
// when it's done erasing, does the same with the next sentence in the array
}
//Usage
typeMultipleSentences('#type-here', ['Hello World', 'Hola Mundo', 'Hallo Welt']);
我共享的对象中的其他函数只是帮助器,它们会做其他类似的事情,比如将光标添加到末尾,一个接一个地输入段落等等。但这些都不重要,我只需要知道如何实现这种“Promise Chaining Delaying thing”
听起来你需要一点延迟功能,你可以通过返回一个在设定时间后解决的承诺来做到这一点,然后使用 async/await
来简化代码,这样你就不会“链接” " 什么都等着。
function delay(time) {
return new Promise(res => {
setTimeout(() => res(), time);
});
}
async function main(data) {
for (const el of data) {
console.log(`Writing ${el}`);
await delay(2000);
console.log(`Erasing ${el}`);
await delay(2000);
}
console.log('Done!');
}
const data = ['Hello World', 'Hola Mundo', 'Hallo Welt'];
main(data);
我正在从头开始构建一个练习项目。这是一个香草 javascript 插件,可让您创建“动画打字效果”。
到目前为止的演示代码笔:https://codepen.io/juan-sebasti-n-bonnett/pen/oNoVGad
const TypingEffect = {
options: {
parallel: false,
showCursor: true,
removeOtherCursors: true,
removeCursorAfter: 0,
cursorColor: '#000',
cursorBG: '#000',
cursorWeight: '0px',
cursorInterval: 400,
typingSpeed: 80,
cursorChar: '|',
eraseSpeed: 50,
showcaseEraseDelay: 3000,
},
type: function(_elem, _str) {
let elem;
if (typeof(_elem) != 'string' && typeof(_elem) != 'object') return;
if (typeof(_elem) == 'string') {
console.log('we have to query a selector');
elem = document.querySelector(_elem);
console.log(elem);
}
if (this.options.showCursor) elem = this.cursor(elem);
let typingSpeed = parseInt(this.options.typingSpeed);
let str = _str || elem.innerHTML;
let strArray = str.split('');
let currentString = '';
let i = 0;
return new Promise((resolve, reject) => {
let typing = setInterval(() => {
if (i < strArray.length) {
currentString = currentString + strArray[i];
elem.innerHTML = currentString;
i++;
} else {
clearInterval(typing);
resolve(elem);
}
}, typingSpeed);
});
},
typeMultiple: function(elements) {
let promise = Promise.resolve();
elements.forEach(element => {
promise = promise.then(() => {
return this.type(element);
});
});
promise.then(() => {
if (parseInt(this.options.removeCursorAfter) > 0) {
setTimeout(() => {
this.removeCursor();
}, this.options.removeCursorAfter);
}
});
return promise;
},
typeSelector: function(selector) {
let elements = document.querySelectorAll(selector);
if (this.options.parallel) {
elements.forEach(element => {
this.type(element);
});;
} else {
return this.typeMultiple(elements);
}
},
erase: function(elem) {
if (typeof(elem) != 'string' && typeof(elem) != 'object') return;
if (typeof(elem) == 'string') {
elem = document.querySelector(elem);
}
let str = elem.innerHTML;
return new Promise((resolve, reject) => {
let erasing = setInterval(() => {
if (str.length > 0) {
str = str.slice(0, -1);
elem.innerHTML = str;
} else {
clearInterval(erasing);
resolve(elem);
}
}, parseInt(this.options.eraseSpeed));
});
},
cursor: function(elem) {
if (this.options.removeOtherCursors) this.removeCursor();
let string = elem.innerHTML;
let cursorInterval = parseInt(this.options.cursorInterval);
let cursorVisible = true;
const cursorNode = document.createElement('span');
const cursor = document.createTextNode(this.options.cursorChar);
const stringNode = document.createElement('span');
const stringContent = document.createTextNode(string);
elem.innerHTML = '';
stringNode.appendChild(stringContent);
elem.appendChild(stringNode);
stringNode.id = 'typing-string-content';
cursorNode.appendChild(cursor);
elem.appendChild(cursorNode);
cursorNode.id = 'typing-cursor';
cursorNode.style.paddingLeft = this.options.cursorWeight;
cursorNode.style.backgroundColor = this.options.cursorBG;
cursorNode.style.color = this.options.cursorColor;
setInterval(() => {
if (cursorVisible) {
cursorNode.style.opacity = 0;
cursorVisible = false;
} else {
cursorNode.style.opacity = 1;
cursorVisible = true;
}
}, cursorInterval);
return stringNode;
},
removeCursor: function(_cursor) {
let cursor = _cursor || document.getElementById('typing-cursor');
if (cursor) cursor.remove();
},
init: function(_options) {
if (_options) Object.assign(this.options, _options);
},
}
/*
Typing Effect usage
*/
let newOptions = {
typingSpeed: 120,
eraseSpeed: 120,
cursorColor: 'rgb(14, 231, 32)',
}
TypingEffect.init(newOptions);
let string = `Hello! I'm using the type() function`;
let done = TypingEffect.type('#type-here', string);
done.then((elem) => {
if (elem) {
let notice = document.getElementById('notice');
notice.innerHTML = `Done Typing! But I will be erased after 6 seconds`;
setTimeout(() => {
let doneErasing = TypingEffect.erase(elem);
doneErasing.then((elem) => {
notice.innerHTML = `Done erasing!`;
});
}, 6000);
}
});
* {
box-sizing: border-box;
}
body {
background-color: #000;
}
.container {
width: 100%;
/*display: flex;
align-items: center;
justify-content: center;*/
}
.typing-effect {
font-family: 'Consolas';
color: rgb(14, 231, 32);
font-size: 22px;
}
#notice {
color: white;
font-size: 22px;
font-family: sans-serif;
padding-top: 54px;
}
<div class="container">
<p id="type-here" class="typing-effect"></p>
</div>
<div id="notice"></div>
我已经有了允许我在 HTML 元素中键入字符串的机制和允许我删除它的另一个机制。但我现在的目标是能够创建第三个函数,它接收一个句子数组并开始输入一个句子,然后稍等片刻,擦除它然后输入另一个句子直到数组结束,然后从索引 0.
现在的代码使用 Promise,逻辑如下:
function type(selector, string) {
// returns a promise that resolves
// when we're done typing the string
// and returns the element that contains the text (the selector)
}
function erase(selector) {
// returns a promise that resolves when
// we're done erasing the whole node's content
// and returns the element that is now empty (the selector)
}
现在,我对 promises 这个概念还是陌生的,虽然当用 API 解释它们时它们很容易理解,因此很难链式操作它们,主要是当你需要一些东西的时候像这样:
function typeMultipleSentences(selector, arrayOfSentences) {
// LOOP:
// types the first sentence of the array
// when its done typing, waits for a couple seconds and then erases it
// when it's done erasing, does the same with the next sentence in the array
}
//Usage
typeMultipleSentences('#type-here', ['Hello World', 'Hola Mundo', 'Hallo Welt']);
我共享的对象中的其他函数只是帮助器,它们会做其他类似的事情,比如将光标添加到末尾,一个接一个地输入段落等等。但这些都不重要,我只需要知道如何实现这种“Promise Chaining Delaying thing”
听起来你需要一点延迟功能,你可以通过返回一个在设定时间后解决的承诺来做到这一点,然后使用 async/await
来简化代码,这样你就不会“链接” " 什么都等着。
function delay(time) {
return new Promise(res => {
setTimeout(() => res(), time);
});
}
async function main(data) {
for (const el of data) {
console.log(`Writing ${el}`);
await delay(2000);
console.log(`Erasing ${el}`);
await delay(2000);
}
console.log('Done!');
}
const data = ['Hello World', 'Hola Mundo', 'Hallo Welt'];
main(data);