Sky
Sky

Reputation: 251

nodejs write file frequently

I have a listener to listen for the change of content, once the content modified, it will emit the handler function:

$('#editor').on('onchange', () => changeHandler('...','...'));

function changeHandler(filePath, content){
    var ws = fs.createWriteStream(filePath, 'utf8');
    ws.write(content);
}

My problem is that the 'onchange' occurs too often, so 'write file' too often handles, it may lost data during the period. Can someone give any suggestion?

Update Now I've changed code according the answers below looks like:

this.buffer = null; //used to cache

// once content changed, maybe too often
changeHandler() {        
    if (this.editor.curOp && this.editor.curOp.command.name) {
        var id = $('.nav-items li.active .lk-hosts').attr('data-hosts-id');
        var content = this.editor.getValue();
        // cache data, not immediately write to file
        this.buffer = {id: id, content: content};
    }        
}

setInterval(()=> {
        // means there's data in cache
        if (this.buffer !== null) {
            let id = this.buffer.id;
            let content = this.buffer.content;
            // reset cache to null
            this.buffer = null;
            // write file
            this.writeContent(id, content, (err)=> {
            })
        }
    }, 800);

Thanks all answers!

Upvotes: 1

Views: 502

Answers (3)

yelsayed
yelsayed

Reputation: 5532

Why not simply build a buffer to collect written text then write to file only when you have a certain number of writes:

$('#editor').on('onchange', () => changeHandler('...','...'));

var writeBuffer = ''; // can also make this an array
var writeBufferSize = 0;
var filePath = 'path_to_file';
var ws = fs.createWriteStream(filePath, 'utf8');

function changeHandler(content){
    if (writeBufferSize == SOME_THRESHOLD) {
        ws.write(writeBuffer);
        writeBuffer = '';
        writeBufferSize = 0;
    } else {
        writeBuffer += content + '\n';
        writeBufferSize++;
    }
}

If you choose a write buffer threshold that's too big, you might want to delegate the write to some worker thread to be done in parallel, and in this case you can create another temporary write buffer to fill out while the original is being written, then switch the two.

Upvotes: 1

Irina Rapoport
Irina Rapoport

Reputation: 334

The Underscore library has _.throttle() and _.debounce() functions.

Upvotes: 0

Medet Tleukabiluly
Medet Tleukabiluly

Reputation: 11930

This sample below shows how to make debounced event handling, although it's not node.js code it's same in concept.

// eventEmitter variable to use
var emitter = new EventEmitter();

// dom element change event
$('#editor').on('input', function(event) {
  emitter.emit('changeEvent', event.target.value);
});

// event listener, which debounces change event of input
emitter.on('changeEvent', debounce(function(data) {
  writeFile('li', data);
}, 1000)); // <== debounce for 1second


// sample emitter, for demo
// we don't have access to nodejs EventEmitter class in Stackoverflow 
// don't use in production
function EventEmitter() {
  var callbacks = [];
  return {
    on: function(eventName, fn) {
      callbacks.push({
        eventName: eventName,
        callback: fn
      })
    },
    emit: function(eventName, payload) {
      var fn = callbacks.find(function(item) {
        return item.eventName === eventName;
      });
      if (fn) {
        fn.callback(payload);
      }
    }
  }
}

// simple logger for demo purpose
// emulates write file 
function writeFile(name, content) {
  var $elem = $(document.createElement(name));
  $elem.text(content);
  $('#logger').append($elem);
}

// throttle function - reduces fn call with timeout
// credits: https://remysharp.com/2010/07/21/throttling-function-calls
function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="editor" placeholder="Enter text, this will emit change event"></textarea>

<p>
  Notice the 1sec throttle (write something, pause for 1sec, write again)
</p>

<ul id="logger"></ul>

The debounce function can be also used on textarea change event

// debounce emitting
$('#editor').on('input', debounce(function(event) {
  emitter.emit('changeEvent', event.target.value);
}, 1000));

// write file when received event without debounce
emitter.on('changeEvent', function(data){
  logElement('li', data);
});

Upvotes: 0

Related Questions