在forEach循环中显示具有延迟的数组元素

时间:2022-07-05 21:36:31

For a client, I need to use a typewriter effect to display four different lines of text from an array

对于客户端,我需要使用打字机效果来显示数组中的四行不同的文本

I have my typewriter effect setup well, but I'm at a loss on why my forEach loop only displays the last element from my array

我的打字机效果设置得很好,但是我为什么我的forEach循环只显示我的数组中的最后一个元素我感到很遗憾

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");

function go() {
  txt.forEach(function(str, index) {
    text = str; // var to pass to typeWriter
    setTimeout(typeWriter(), delay * index);
  });
}

function typeWriter() {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML = '';
      i = 0;
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

5 个解决方案

#1


2  

You need to pass callback functions to loop over your array's items

您需要传递回调函数来循环遍历数组的项目

Look at this code snippet

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 1000
var demo = document.getElementById("demo");

function go() {
  function loop(index) {
    if (index === txt.length) return;
       
    setTimeout(function() {
      text = txt[index];    
      typeWriter(function() {
        loop(++index);
      });      
    }, delay * index);
  }

  loop(0);
}

function typeWriter(cb) {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(function() {
      typeWriter(cb);
    }, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML += '<p>';
      i = 0;
      cb();
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

See? now is looping correctly.

看到?现在循环正确。

#2


1  

Just thought it makes a nice question to illustrate async iteration, a brand-new ES2018 feature:

只是认为它是一个很好的问题来说明异步迭代,一个全新的ES2018功能:

async function delay(value, time) {
    return new Promise(resolve => setTimeout(() => resolve(value), time));
}

async function *eachDelayed(values, time) {
    for (let value of values)
        yield await delay(value, time);
}

async function print(xs) {
    for await (let x of eachDelayed(xs, 50))
        document.body.innerHTML += x;
}

print('The quick brown fox jumps over the lazy dog!')

You need the latest Chrome/FF to run this.

你需要最新的Chrome / FF来运行它。

#3


0  

Your line to update the UI is:

您更新UI的行是:

demo.innerHTML = '';

But needs to be:

但需要:

demo.innerHTML += '<br>';

So that you don't overwrite the previous output and you place the new output on a new line.

这样您就不会覆盖以前的输出,而是将新输出放在新行上。

Also, you are actually invoking the typewriter function immediately because your setTimeout looks like this:

此外,您实际上是立即调用打字机功能,因为您的setTimeout如下所示:

setTimeout(typeWriter(), delay * index);

Instead of just referencing the function, like this:

而不是仅仅引用该函数,如下所示:

setTimeout(typeWriter, delay * index);

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");

function go() {
  txt.forEach(function(str, index) {
    text = str; // var to pass to typeWriter
    setTimeout(typeWriter, delay * index);
  });
}

function typeWriter() {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML += '<br>';
      i = 0;
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

#4


0  

forEach is not delayed. When typeWriter is executed, it will see text = 'Lorem ipsum dummy TEXT blabla.'

forEach没有延迟。当执行typeWriter时,它会看到text ='Lorem ipsum dummy TEXT blabla'。

My not so optimized recommendation:

我没有那么优化的建议:

var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];

function go(){
  demo.innerHTML = '';
  txt.length && doType();
}

function doType(){

  if(!txt[0].length){ 
    txt.shift(); 
    demo.innerHTML += '<br />';
    return setTimeout( doType, 50 );
  }

  if(!txt.length) return;

  demo.innerHTML += txt[0][0];
  txt[0] = txt[0].substr(1);

  setTimeout( doType, 50 );
}

I believe you can do the necessary modification.

我相信你可以做必要的修改。

#5


0  

Here is a solution that combines Promises with a setTimeout offset controller.

这是一个将Promises与setTimeout偏移控制器结合在一起的解决方案。

const speed = 50;
const delay = 2000;
      
const demo = document.getElementById("demo");

function go() {
	
  const textArray = [
    'Lorem ipsum dummy text blabla.',
    'Lorem IPSUM dummy text blabla.',
    'Lorem ipsum DUMMY text blabla.',
    'Lorem ipsum dummy TEXT blabla.'
  ];
	
  let linesToType = initializePromises(textArray)
	
  linesToType.reduce((promiseChain, currentLine) => {
    return promiseChain.then(currentLine);
  }, Promise.resolve())
  
}

var offsetController = (function() {
  let _offset = 0;
  return {
    increaseOffset(time) {
      _offset = _offset + time
    },
    getOffset(){
      return _offset;
    }
  }
})()

function initializePromises(textArray) {
  return textArray.map(line => typeWriter(line) )
}
	
function typeWriter(text) {
  offsetController.increaseOffset(delay);
  return new Promise((resolve, reject) => {
    let promises = text.split("").map((letter, index) => {
	  return printNextLetter(demo, text, index)
    })
    clearLine(demo);
    Promise.all(promises).then(() => { 
      resolve() 
    })
  })
}

function printNextLetter(element, text, i) {
  offsetController.increaseOffset(speed);
  return new Promise((resolve) => setTimeout(() => {
    element.innerHTML += text.charAt(i);
    resolve()
  }, offsetController.getOffset()));
}

function clearLine(element) {
  offsetController.increaseOffset(delay);
  return new Promise((resolve) => setTimeout(() => {
    element.innerHTML = "";
    resolve()
  }, offsetController.getOffset()));
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

#1


2  

You need to pass callback functions to loop over your array's items

您需要传递回调函数来循环遍历数组的项目

Look at this code snippet

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 1000
var demo = document.getElementById("demo");

function go() {
  function loop(index) {
    if (index === txt.length) return;
       
    setTimeout(function() {
      text = txt[index];    
      typeWriter(function() {
        loop(++index);
      });      
    }, delay * index);
  }

  loop(0);
}

function typeWriter(cb) {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(function() {
      typeWriter(cb);
    }, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML += '<p>';
      i = 0;
      cb();
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

See? now is looping correctly.

看到?现在循环正确。

#2


1  

Just thought it makes a nice question to illustrate async iteration, a brand-new ES2018 feature:

只是认为它是一个很好的问题来说明异步迭代,一个全新的ES2018功能:

async function delay(value, time) {
    return new Promise(resolve => setTimeout(() => resolve(value), time));
}

async function *eachDelayed(values, time) {
    for (let value of values)
        yield await delay(value, time);
}

async function print(xs) {
    for await (let x of eachDelayed(xs, 50))
        document.body.innerHTML += x;
}

print('The quick brown fox jumps over the lazy dog!')

You need the latest Chrome/FF to run this.

你需要最新的Chrome / FF来运行它。

#3


0  

Your line to update the UI is:

您更新UI的行是:

demo.innerHTML = '';

But needs to be:

但需要:

demo.innerHTML += '<br>';

So that you don't overwrite the previous output and you place the new output on a new line.

这样您就不会覆盖以前的输出,而是将新输出放在新行上。

Also, you are actually invoking the typewriter function immediately because your setTimeout looks like this:

此外,您实际上是立即调用打字机功能,因为您的setTimeout如下所示:

setTimeout(typeWriter(), delay * index);

Instead of just referencing the function, like this:

而不是仅仅引用该函数,如下所示:

setTimeout(typeWriter, delay * index);

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");

function go() {
  txt.forEach(function(str, index) {
    text = str; // var to pass to typeWriter
    setTimeout(typeWriter, delay * index);
  });
}

function typeWriter() {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML += '<br>';
      i = 0;
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

#4


0  

forEach is not delayed. When typeWriter is executed, it will see text = 'Lorem ipsum dummy TEXT blabla.'

forEach没有延迟。当执行typeWriter时,它会看到text ='Lorem ipsum dummy TEXT blabla'。

My not so optimized recommendation:

我没有那么优化的建议:

var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];

function go(){
  demo.innerHTML = '';
  txt.length && doType();
}

function doType(){

  if(!txt[0].length){ 
    txt.shift(); 
    demo.innerHTML += '<br />';
    return setTimeout( doType, 50 );
  }

  if(!txt.length) return;

  demo.innerHTML += txt[0][0];
  txt[0] = txt[0].substr(1);

  setTimeout( doType, 50 );
}

I believe you can do the necessary modification.

我相信你可以做必要的修改。

#5


0  

Here is a solution that combines Promises with a setTimeout offset controller.

这是一个将Promises与setTimeout偏移控制器结合在一起的解决方案。

const speed = 50;
const delay = 2000;
      
const demo = document.getElementById("demo");

function go() {
	
  const textArray = [
    'Lorem ipsum dummy text blabla.',
    'Lorem IPSUM dummy text blabla.',
    'Lorem ipsum DUMMY text blabla.',
    'Lorem ipsum dummy TEXT blabla.'
  ];
	
  let linesToType = initializePromises(textArray)
	
  linesToType.reduce((promiseChain, currentLine) => {
    return promiseChain.then(currentLine);
  }, Promise.resolve())
  
}

var offsetController = (function() {
  let _offset = 0;
  return {
    increaseOffset(time) {
      _offset = _offset + time
    },
    getOffset(){
      return _offset;
    }
  }
})()

function initializePromises(textArray) {
  return textArray.map(line => typeWriter(line) )
}
	
function typeWriter(text) {
  offsetController.increaseOffset(delay);
  return new Promise((resolve, reject) => {
    let promises = text.split("").map((letter, index) => {
	  return printNextLetter(demo, text, index)
    })
    clearLine(demo);
    Promise.all(promises).then(() => { 
      resolve() 
    })
  })
}

function printNextLetter(element, text, i) {
  offsetController.increaseOffset(speed);
  return new Promise((resolve) => setTimeout(() => {
    element.innerHTML += text.charAt(i);
    resolve()
  }, offsetController.getOffset()));
}

function clearLine(element) {
  offsetController.increaseOffset(delay);
  return new Promise((resolve) => setTimeout(() => {
    element.innerHTML = "";
    resolve()
  }, offsetController.getOffset()));
}
<button onclick="go()">Click me</button>

<p id="demo"></p>