co icon indicating copy to clipboard operation
co copied to clipboard

co.defer feature request

Open rumkin opened this issue 10 years ago • 7 comments

What's about golang-like deferred calls with co? I think it should looks like that:

co(function *(){
    var db = yield DB.connect();
    // Add deferred function
    yield co.defer(function(){
        db.close();
    }); 

    var doc = db.collection('collection').findById(1);

    if (! doc) {
        throw new Error('Document not found');
    }

    doc.counter += 1;

    yield doc.save();
});

In this way db connection will be closed in any way after throwing or returning. Yielding of methods wrapped in defer should return control immediately and put deferred call into deferred stack.

rumkin avatar Oct 02 '15 14:10 rumkin

What advantage does defer give you over simply doing this?

co(function*() {
  // code
}).catch(err => {
  db.close();
});

If we can get the same functionality with the existing API, adding a syntactically pretty method like defer might add additional unnecessary complexity. Please let me know if I am misunderstanding the defer method.

joshbeam avatar Oct 02 '15 17:10 joshbeam

Defer make generator function atomic. It could be passed with variable, it share closed scope and thus it is standalone independent method. If I could do so with usual promise behaviour it will look like this:


function example() {
   var db;
   return co(function *(){
      db = yield DB.connect();
      // do the job ...
   }).then(function(){
       db.close();
   }).catch(function(){
       db.close();
   });
};

As you see we have three separated code blocks which shares same responsibility. But if I wish more complex behaviour It would became into mess of promises.

rumkin avatar Oct 03 '15 09:10 rumkin

What you're looking for is finally

felixfbecker avatar Oct 25 '15 16:10 felixfbecker

@felixfbecker finally or .finally()?

rumkin avatar Oct 26 '15 17:10 rumkin

choose one:

co-style

co(function *() {
  const db = yield DB.connect();
  try {
    yield db.query(...); // do the job, might throw in case of error
  } finally {
    db.close(); // but always close db, no matter if error or not
  }
});

Promise-style

DB.connect()
  .then(db =>
    db.query(...)
      .then(() => db.anotherQuery(...))
      .finally(() => {
        db.close();
      })
  })

(Promise have a problem when you need to access something of the scope in a previous then handler, like the db. So you can either assign db to a variable db out of the scope like you did or nest your promise handlers like my example)

felixfbecker avatar Oct 26 '15 19:10 felixfbecker

@rumkin Can you explain why should co.defer be yield-able. Doesn't co.defer be enough without yield

IdpugantiSanjay avatar Mar 18 '20 08:03 IdpugantiSanjay

@IdpugantiSanjay to properly associate deferred code with the currently executing function. And if the function has several block of code which should be executed in a finally statement it could affect performance significantly due to nested try/catch blocks.

rumkin avatar Mar 18 '20 10:03 rumkin