Wait for an append within a cycle

0

I have the following code, what I want is that I go through each one of the registers inside the array data and show it, put the state READ , then PROCESSED , finally DELETED and continue with the other until the end of the cycle, the code that I have does everything at once with all the records, I have tried to correct this with this function sleep but I can not find a concrete result

function sleep(f, m) {
  setTimeout(() => {
    f();
  }, m)
}

var data = [
  'e48c56b4-aebb-4499-a7c5-64db40381183',
  'eb1cf18c-fbdb-4b0f-b775-34e4542a8675',
  '62afe7bd-2061-4121-ab50-0b181e00e85a',
  '943f1ae4-60cd-424d-94d2-7e3f66a88e1e',
  'e6140cb0-b9c9-488a-8469-f9c3a51faffd',
  '9c6b24e8-1b53-4d68-be98-672b74380491',
  'f160a7d7-7609-44c9-9839-17ebd491ea21'
];

for (var i = 0; i < data.length; i++) {
  $('.super').append(data[i] + '<span class="pull-right label label-success">LEÍDO</span><br/>');
  sleep(() => {
    $('.super').children('span').removeClass('label-success');
    $('.super').children('span').addClass('label-warning');
    $('.super').children('span').html('PROCESADO');
  }, 2000);

  sleep(() => {
    $('.super').children('span').removeClass('label-warning');
    $('.super').children('span').addClass('label-danger');
    $('.super').children('span').html('ELIMINADO');
  }, 4000);
}
.super {
  width: auto;
  height: 150px;
  border: 1px solid black;
  padding: 5px 5px 5px 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<br />
<br />
<br />
<div class='container'>
  <div class='super'></div>
</div>
    
asked by Jorius 10.03.2017 в 19:53
source

2 answers

2

One way is to treat the array data as a queue that is being consumed from another function. The function checks if the queue is empty (in which case it ends) and otherwise removes the last element, processes it, and executes again with the remaining elements.

var data = [
  'e48c56b4-aebb-4499-a7c5-64db40381183',
  'eb1cf18c-fbdb-4b0f-b775-34e4542a8675',
  '62afe7bd-2061-4121-ab50-0b181e00e85a',
  '943f1ae4-60cd-424d-94d2-7e3f66a88e1e',
  'e6140cb0-b9c9-488a-8469-f9c3a51faffd',
  '9c6b24e8-1b53-4d68-be98-672b74380491',
  'f160a7d7-7609-44c9-9839-17ebd491ea21'
];

function sleep(f, m) {
  setTimeout(() => {
    f();
  }, m)
}

function procesar_y_eliminar() {
  if(data.length===0) {
     return;
  } 
  var registro=data.pop();
  var elemento=jQuery('<span class="pull-right label label-success">LEÍDO</span><br/>');
  $('.super').append(registro).append(elemento);
  sleep(() => {
    elemento.removeClass('label-success');
    elemento.addClass('label-warning');
    elemento.html('PROCESADO');
  }, 2000);

  sleep(() => {
    elemento.removeClass('label-warning');
    elemento.addClass('label-danger');
    elemento.html('ELIMINADO');
  }, 4000);
  
  sleep(()=> {
    procesar_y_eliminar();
  },5000);
}
 
procesar_y_eliminar(); 
.super {
  width: auto;
  height: 150px;
  border: 1px solid black;
  padding: 5px 5px 5px 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<br />
<br />
<br />
<div class='container'>
  <div class='super'></div>
</div>

This same behavior could be shown a bit more intuitively if the sleep function returned a promise, such that we could do then on its output. It's like intuitive to use then to indicate that one thing happens after another.

This is the same example but promisificating the sleep. Note that now the contents of procesar_y_eliminar are not two sleep with different intervals, but one followed by the other by then . This would allow to execute not only two but N steps on each object without generating more nesting or having to calculate the interval for each one.

var data = [
  'e48c56b4-aebb-4499-a7c5-64db40381183',
  'eb1cf18c-fbdb-4b0f-b775-34e4542a8675',
  '62afe7bd-2061-4121-ab50-0b181e00e85a',
  '943f1ae4-60cd-424d-94d2-7e3f66a88e1e',
  'e6140cb0-b9c9-488a-8469-f9c3a51faffd',
  '9c6b24e8-1b53-4d68-be98-672b74380491',
  'f160a7d7-7609-44c9-9839-17ebd491ea21'
];

function sleep(m) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, m)
    });
}

function procesar_y_eliminar() {
  if (data.length === 0) {
     return;
  }
  var registro = data.pop();
  var elemento = $('<span class="pull-right label label-success">LEÍDO</span><br/>');
  $('.super')
    .append(registro)
    .append(elemento);
  return sleep(2000)
    .then(() => {
        elemento
            .removeClass('label-success')
            .addClass('label-warning')
            .html('PROCESADO');
        return sleep(2000);
    }).then(() => {
        elemento
            .removeClass('label-warning')
            .addClass('label-danger')
            .html('ELIMINADO');
        return sleep(2000);
    }).then(() => {
        return procesar_y_eliminar();
    });

}

procesar_y_eliminar();
.super {
  width: auto;
  height: 150px;
  border: 1px solid black;
  padding: 5px 5px 5px 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<br />
<br />
<br />
<div class='container'>
  <div class='super'></div>
</div>

There are more elaborate ways (although not necessarily better ones). I've taken over the bluebird library (faster and more versatile than the native promises), which has the Promise.map which allows you to execute promises with a certain concurrency index. You can run one, two, etc., all at the same time.

I think in this case using promises arrays is unnecessary and would serve only as a proof of concept. Actually the most efficient thing would be to look for a queue library like queue or d3-queue that are specifically for this use case.

    
answered by 10.03.2017 / 20:09
source
2

The problem is that you are thinking about JavaScript with a different approach. In JavaScript there is not something like Thread#sleep of Java where the current thread "sleeps", that is, stops its execution for a period of time. In JavaScript you can not do that.

To achieve your goal simply apply recursion, calling the function once the record has been read.

Example

let data = [
  'e48c56b4-aebb-4499-a7c5-64db40381183',
  'eb1cf18c-fbdb-4b0f-b775-34e4542a8675',
  '62afe7bd-2061-4121-ab50-0b181e00e85a',
  '943f1ae4-60cd-424d-94d2-7e3f66a88e1e',
  'e6140cb0-b9c9-488a-8469-f9c3a51faffd',
  '9c6b24e8-1b53-4d68-be98-672b74380491',
  'f160a7d7-7609-44c9-9839-17ebd491ea21'
];

let tbody = document.querySelector('tbody');

start();

function start(i = 0) {
  if (i === data.length) {
    return;
  }

  let tr = document.createElement('tr');
  let status = document.createElement('td');
  let uuid = document.createElement('td');

  uuid.textContent = data[i];
  status.textContent = 'Leído';
  status.className = 'label-success';

  tr.appendChild(status);
  tr.appendChild(uuid);
  tbody.appendChild(tr);

  proccess(status)
    .then(() => {
      remove(status)
        .then(() => start(i + 1));
    });
}

function proccess(el) {
  return new Promise((resolve) => {
    setTimeout(() => {
      el.classList.remove('label-success');
      el.classList.add('label-warning');
      el.textContent = 'Procesado';
      resolve();
    }, 2000);
  });
}

function remove(el) {
  return new Promise((resolve) => {
    setTimeout(() => {
      el.classList.remove('label-warning');
      el.classList.add('label-danger');
      el.textContent = 'Eliminado';
      resolve();
    }, 2000);
  });
}
td:first-of-type {
  color: #fff;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

<table class="table table-bordered table-striped">
  <thead>
    <tr>
      <th>Status</th>
      <th>UUID</th>
    </tr>
  </thead>
  <tbody>
    <tr></tr>
  </tbody>
</table>
    
answered by 10.03.2017 в 20:18