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.

Edson Magombe
  • 313
  • 2
  • 14
user944513
  • 12,247
  • 49
  • 168
  • 318
  • Are you trying to re-implement jQuery? If so, why? Does reading through [the jQuery source code](https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/queue/delay.js) solve your problem? – Lionel Rowe Aug 14 '20 at 09:52
  • Does this answer your question? [How to set time delay in javascript](https://stackoverflow.com/questions/17883692/how-to-set-time-delay-in-javascript) – Liam Aug 14 '20 at 10:03
  • @ i don't want to use `asyn await` .promise i can use – user944513 Aug 14 '20 at 10:46

4 Answers4

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>
VLAZ
  • 26,331
  • 9
  • 49
  • 67
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>
Akshay Bande
  • 2,491
  • 2
  • 12
  • 29
  • 1
    Promises are fine to use, but then OP cannot directly chain another `delay`/`addClass` like in their sample code. – TiiJ7 Aug 14 '20 at 10:00
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>
pintxo
  • 2,085
  • 14
  • 27
  • this will fail in this case `$('#test').delay(1000).addClass('red').delay(5000).removeClass('red').addClass('red')` – user944513 Aug 14 '20 at 10:44
  • expected output is `add class red after 1 sec and wait for 5 sec remove class and add class red without wait` – user944513 Aug 14 '20 at 10:44
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!");
    });
});
Edson Magombe
  • 313
  • 2
  • 14