如何将来自不同函数的承诺链接未知次数(迭代数组)

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