Home > front end >  How to asynchronously call a function with setTimeout twice
How to asynchronously call a function with setTimeout twice

Time:10-14

I'm trying to create a typewriter effect with setTimeout(). My code for the typewriter function is as follows:

function typeWriter(toWrite,isDelete=false){
    if(!isDelete && i<toWrite.length){
        document.getElementById("typewrite").innerHTML =toWrite.charAt(i);
        i  ;
        speed=Math.random()*100 100;
        setTimeout(typeWriter,speed,toWrite,false);       
    }
    else if(isDelete && i<toWrite.length){
        var typewrite=document.getElementById("typewrite");
        typewrite.innerHTML=typewrite.innerHTML.slice(0,-1);
        i  ;
        speed=100;
        setTimeout(typeWriter,speed,toWrite,true);     
    }  
}
 

And I want to call the code twice, once to write a string, and then a second time to delete a part of it.

My grasp on promises is still very shaky, and my attempt (below) didn't really change anything:

const intro=new Promise((resolve,reject)=>{
    resolve();
})

intro
    .then(typeWriter("hello world"))
    .then(typeWriter("world",true))

When I run the code, instead of having "Hello world" get typed and then delete the "world". Both functions start going synchronously and the final output is "world".

I've been banging my head on this for longer than I'm comfortable admitting, I would appreciate any help.

Thanks!

CodePudding user response:

to be chainable, typeWriter has to return a function.

e.g.

function typeWriter(toWrite,isDelete=false) {
   return new Promise((resolve, reject) => {


   })
}

If you were to use a setTimout, and you want to wait for the function to be called before going to the next of the chain, place the resolve in the settimeout.

e.g.

function typeWriter(toWrite,isDelete=false) {
   return new Promise((resolve, reject) => {
      setTimeout(() => {
          resolve("hello i am done")
      }, 1000)

   })
}

CodePudding user response:

Note, I'm using the same syntax as you - e.g

.then(typeWriter("hello world"))

But in this case, typeWriter returns a function, so it's legit

const typeWriter = (toWrite, isDelete) => {
    const typewrite=document.getElementById("typewrite");
    if (!isDelete) {
        const fn = async (position = 0) => {
            if (position < toWrite.length) {
                typewrite.innerHTML  = toWrite.charAt(position);
                await new Promise(resolve => setTimeout(resolve, Math.random()*100   100));
                return fn(position 1);
            }
        };
        return () => fn(0);
    } else {
        const fn = async (position) => {
            if (position < toWrite.length) {
            
                typewrite.innerHTML = typewrite.innerHTML.slice(0,-1);
                await new Promise(resolve => setTimeout(resolve, 100));
                return fn(position   1);
            }
        };
        return () => fn(0);
    }
};
Promise.resolve()
.then(typeWriter("hello world"))
.then(typeWriter("world",true))
<div id="typewrite"></div>

CodePudding user response:

You're calling typeWriter immediately, while you really intended to pass that call to the then method, and have that only called when the relevant promise resolves. You can do that by passing () => typeWriter(......) as anonymous function to then.

Secondly, typeWriter will then have to return a promise, so the next chained then callback will only be called when that promise resolves.

I would also change the signature of your function a bit, so that it first deletes a given number of characters (can be 0) and then inserts the given string.

Here is how that looks:

// Promisify setTimeout:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

// Make async, so it returns a promise:
const typeWriter = async (numDelete, toWrite) => {
    const typewrite = document.getElementById("typewrite");
    // First delete a given number of characters
    while (numDelete-- > 0) {
        typewrite.textContent = typewrite.textContent.slice(0, -1);
        await delay(100);
    }
    // Then append new characters
    for (const letter of toWrite) {
        typewrite.textContent  = letter;
        await delay(Math.random()*100   100);
    }
};

Promise.resolve()
       // Pass a callback -- don't call typeWriter yourself
       .then(() => typeWriter(0, "hello world"))
       .then(() => typeWriter(5, ""));
<div id="typewrite"></div>

  • Related