3
function abc(elm){
    this.$elm =  document.querySelector(elm)
}

abc.prototype.addClass =  function (str){
  this.$elm.classList.add(str)
    return this
}

abc.prototype.removeClass =  function (str){
   this.$elm.classList.remove(str)
    return this
}

abc.prototype.delay =  function (timer){
   let self = this
  
  setTimeout(()=>{
    return self
  },timer)
    return this
}

function $(str){
  return new abc(str);
}

let x = $('#test').delay(5000).delay(1000).addClass('red');

console.log($('#test'));

I want to add red class after 6 secs.I tried like using setTimeout but not work.could you please suggest the better way ?

I want to write a delay function which delay for sometime before proceeding/executing next code.

3
  • Are you trying to re-implement jQuery? If so, why? Does reading through the jQuery source code solve your problem? Commented Aug 14, 2020 at 9:52
  • Does this answer your question? How to set time delay in javascript Commented Aug 14, 2020 at 10:03
  • @ i don't want to use asyn await .promise i can use Commented Aug 14, 2020 at 10:46

4 Answers 4

3

You can make a very simple queue of tasks to be executed based off promises. Since the promise execution already uses a task queue, you just need to keep a single promise and any time you get a new thing to add, you chain it via .then() and keep the latest promise. That way if you add three tasks T1 -> T2 -> T3, they would resolve in the order they were added. If you add a task that just adds a simple delay between them like T1 -> wait 6 seconds -> T2 -> wait 5 seconds -> T3 then that will run also space out the executions.

This is a sample implementation to illustrate the idea that utilises thunks (functions that take no parameters) as task to delay and execute later.

function abc(elm){
    this.$elm =  document.querySelector(elm)
    this.queue = Promise.resolve();
}

/**
 * Uniform way of adding a task for later execution
 * @param {Function} task - a thunk to be executed later
 * @param {number} [delay=0] time in milliseconds to wait after last task finished before executing this on
 */
abc.prototype.addTask = function(task, delay = 0) {
  const waitFor = () => new Promise( res => setTimeout(res, delay) );
  
  this.queue = this.queue
        .then(waitFor)
        .then(task)
}

abc.prototype.addClass =  function (str){
  this.addTask(() => this.$elm.classList.add(str));
  return this
}

abc.prototype.removeClass =  function (str){
  this.addTask(() => this.$elm.classList.remove(str));
  return this
}

abc.prototype.delay =  function (timer){
  // add an empty function as a task. If needed this can also do logging or other internal logic
  this.addTask(() => {}, timer);
  return this
}

function $(str){
  return new abc(str);
}

//usage

let x = $('#test').delay(5000).delay(1000).addClass('red');

x.delay(1000)
  .delay(1000)
  .delay(1000)
  .delay(1000)
  .delay(1000) //5 seconds
  .removeClass('red');
.red {
  background-color: red;
  color: white;
}
<p id="test">
Bacon ipsum dolor amet hamburger t-bone pork, pastrami sirloin swine corned beef tenderloin frankfurter tail ball tip meatball pork belly spare ribs prosciutto. Bresaola turkey buffalo jowl t-bone biltong burgdoggen cow capicola meatball pastrami boudin alcatra. Bresaola chicken bacon cow, frankfurter meatball hamburger jerky. Shankle capicola chicken leberkas turkey. Ball tip bacon doner kielbasa jerky. Salami picanha chicken bacon, turducken buffalo chislic andouille porchetta tongue shankle prosciutto t-bone. Beef andouille cow pork chop alcatra, turducken ribeye sirloin tail boudin strip steak doner.
</p>

Sign up to request clarification or add additional context in comments.

Comments

1

You need promise.

abc.prototype.delay = function (timer) {
    return new Promise((resolve) => {
        let self = this
        setTimeout(() => {
            return resolve(self)
        }, timer)
        return resolve(this);
    })

}
let x = $('#test').delay(5000);

Please find below example.

function abc(elm) {
  this.$elm = document.querySelector(elm)
}

abc.prototype.addClass = function(str) {
  this.$elm.classList.add(str)
  return this
}

abc.prototype.removeClass = function(str) {
  this.$elm.classList.remove(str)
  return this
}

abc.prototype.delay = function(timer) {
  return new Promise((resolve) => {
    let self = this
    setTimeout(() => {
      return resolve(self)
    }, timer)
  })
}

function $(str) {
  return new abc(str);
}


async function hello() {
  let x = $('body')
  await x.delay(5000);
  x.addClass('red');
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<style>
  .red {
    background: red;
  }
</style>

<body>
  <button onclick="hello()">Click here...</button>
</body>

</html>

1 Comment

Promises are fine to use, but then OP cannot directly chain another delay/addClass like in their sample code.
0

You just can't do that throught SYNCRONOUS way. That would stop the whole app for that time out

The best way is using ASYNCRONOUS and callbacks

abc.prototype.delay = function (timer, callback){
    let self = this;
    setTimeout(() => {
        callback(self);
    }, timer);
}
$('#test').delay(5000, function(element) {
    $(element).delay(5000, function(element) {
        console.log("Here we are!");
    });
});

Comments

0

You cannot delay the execution that way.

Your original delay function, does create a timeout callback, but it also immediately (before the timeout happens) returns itself.

For your example:

$('#test').delay(5000).delay(1000).addClass('red');

the following happens:

  1. call $
  2. call delay on the result of #1
  3. call delay on the result of #2
  4. call addClass on the result of #3
  5. the timeout set in #3 executes and no one cares about the returned result
  6. the timeout set in #2 executes and no one cares about the returned result

To get the result you desire, you could simply accumulate the desired delay, and apply it at the point of the action, like so:

function abc(elm){
    this.$elm =  document.querySelector(elm);
    this._delayTimeout = 0;
    this._delayExec = (cb) => {
      window.setTimeout(cb, this._delayTimeout);
    }
}

abc.prototype.addClass =  function (str){
  
  this._delayExec(() => {
    this.$elm.classList.add(str)
  });
  
  return this;
  
}

abc.prototype.removeClass =  function (str){
   
  this._delayExec(() => {
    this.$elm.classList.remove(str)
  });
   
  return this;
  
}

abc.prototype.delay =  function (timer) {
  this._delayTimeout += timer;
  console.log(this._delayTimeout);
  return this;
}

function $(str){
  return new abc(str);
}

let x = $('#test').delay(1000).addClass('red').delay(2000).removeClass('red').addClass('green');

console.log($('#test'));
.red {
  color: red;
}

.green {
  background-color: green;
}
<div id="test">Test</div>

2 Comments

this will fail in this case $('#test').delay(1000).addClass('red').delay(5000).removeClass('red').addClass('red')
expected output is add class red after 1 sec and wait for 5 sec remove class and add class red without wait

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.