admin管理员组

文章数量:1405553

I have the function "showElement" that has a setTimeout. How can I make that the calls to the function in the eventListener of the 'btnSend' execute one right after the other one?

I tried with .then() but didn't work.

document.getElementById('btnSend').addEventListener('click', e => {
    e.preventDefault();
    result = validateInputs(email, subject, message);
    if(result) {
        showElement(document.getElementById('formGroupSpinner'), 2000);
        showElement(document.getElementById('formGroupSuccessImg'), 2000);
        resetForm();
    }
});

//expects an html element and a number representing miliseconds. Shows the html element for that amount of time.
function showElement(element, mSeconds) {
    element.classList.remove('d-none');
    element.classList.add('d-block');
    setTimeout( () => {
        element.classList.remove('d-block');
        element.classList.add('d-none');
    }, mSeconds);
}

Both functions execute at the same time.

I have the function "showElement" that has a setTimeout. How can I make that the calls to the function in the eventListener of the 'btnSend' execute one right after the other one?

I tried with .then() but didn't work.

document.getElementById('btnSend').addEventListener('click', e => {
    e.preventDefault();
    result = validateInputs(email, subject, message);
    if(result) {
        showElement(document.getElementById('formGroupSpinner'), 2000);
        showElement(document.getElementById('formGroupSuccessImg'), 2000);
        resetForm();
    }
});

//expects an html element and a number representing miliseconds. Shows the html element for that amount of time.
function showElement(element, mSeconds) {
    element.classList.remove('d-none');
    element.classList.add('d-block');
    setTimeout( () => {
        element.classList.remove('d-block');
        element.classList.add('d-none');
    }, mSeconds);
}

Both functions execute at the same time.

Share edited Feb 13, 2019 at 13:49 jo_va 14k3 gold badges25 silver badges49 bronze badges asked Feb 13, 2019 at 13:42 Floro VarelaFloro Varela 691 silver badge9 bronze badges 4
  • 2 Hello Floro, you seems new to JS, look for JavaScript callbacks. – Mehdi Benmoha Commented Feb 13, 2019 at 13:46
  • Please add your HTML too :) – jo_va Commented Feb 13, 2019 at 13:47
  • 2 @jo_va not relevant – Adelin Commented Feb 13, 2019 at 13:47
  • 1 To use then, you need to create and return a promise – Bergi Commented Feb 13, 2019 at 13:47
Add a ment  | 

5 Answers 5

Reset to default 3

There's many different approaches to this but I would suggest using a Promise like this:

document.getElementById('btnSend').addEventListener('click', e => {
    e.preventDefault();
    var result = validateInputs(email, subject, message);
    if(result){
        showElement(document.getElementById('formGroupSpinner'), 2000).then(()=>{
            return showElement(document.getElementById('formGroupSuccessImg'), 2000);
        }).then(()=>{
            resetForm();
        });
    }
});

//expects an html element and a number representing miliseconds. Shows the html element for that amount of time.
function showElement(element, mSeconds) {
    return new Promise((resolve, reject) => {
        element.classList.remove('d-none');
        element.classList.add('d-block');
        setTimeout( () => {
            element.classList.remove('d-block');
            element.classList.add('d-none');
            resolve();
        }, mSeconds);
    });
}

Basically, the function after the .then() only gets executed once you called the resolve();.

Alternatively you could also use a callback or async / await.

you could use a callback to execute other instructions after the function has ended:

//First you add a param "callback" to the function
function showElement(element, mSeconds, callback) {
    element.classList.remove('d-none');
    element.classList.add('d-block');
    setTimeout( () => {
        element.classList.remove('d-block');
        element.classList.add('d-none');
        //Then you ask if the callback function is present and call it
        if(callback && typeof callback === "function") callback();
    }, mSeconds);
}

//Then use it in your event like this:
document.getElementById('btnSend').addEventListener('click', e => {
    e.preventDefault();
    result = validateInputs(email, subject, message);
    if(result) {
        showElement(document.getElementById('formGroupSpinner'), 2000, () => {
            showElement(document.getElementById('formGroupSuccessImg'), 2000);
            resetForm();
        });  
    }
}); 

You can do this by using Promise. You have to wrap your showElement() function into a promise and call the promise resolver once setTimeout fires. Then your calling code can pipe your the promises together with then:

showElement(document.getElementById('formGroupSpinner'), 2000).then(() => {
    showElement(document.getElementById('formGroupSuccessImg'), 2000).then(() => {
        resetForm();
    });
});

However, this quickly leads to callback hell.

You can avoid this by using async/await. You mark your function as async, then you can use await within it to wait for the promises to resolve before continuing to the next one:

await showElement(document.getElementById('formGroupSpinner'), 2000);
await showElement(document.getElementById('formGroupSuccessImg'), 2000);
resetForm();

Here is a working demo:

document.getElementById('btnSend').addEventListener('click', async e => {
    e.preventDefault();
    await showElement(document.getElementById('formGroupSpinner'), 2000);
    await showElement(document.getElementById('formGroupSuccessImg'), 2000);
});

function showElement(element, mSeconds) {
  return new Promise((resolve, reject) => {
    element.classList.remove('d-none');
    element.classList.add('d-block');
    setTimeout(() => {
        element.classList.remove('d-block');
        element.classList.add('d-none');
        resolve();
    }, mSeconds);
  });
}
.d-none {
  display: none;
}

.d-block {
  display: block;
}
<button id="btnSend">Send</button>

<div id="formGroupSpinner" class="d-none">Spinner</div>
<div id="formGroupSuccessImg" class="d-none">Success image</div>

The simplest way to do what you want is simply changing the second showElement to 4000 instead of 2000.

That way one will have a timeout of 2 seconds and the other 4 seconds.

To make a promise chain, you first have to have a promise.

function showElement(element, mSeconds) {
  return new Promise(function(resolve,reject){
    element.classList.remove('d-none');
    element.classList.add('d-block');
    setTimeout( () => {
      element.classList.remove('d-block');
      element.classList.add('d-none');
      resolve();
    }, mSeconds);
  }
}

Then you can use showElement().then(/*something here */)

本文标签: asynchronousExecute javascript function after previous one finishesStack Overflow