tj / co

The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)
MIT License
11.88k stars 789 forks source link

co.defer feature request #245

Open rumkin opened 9 years ago

rumkin commented 9 years ago

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.

joshbeam commented 9 years ago

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.

rumkin commented 9 years ago

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.

felixfbecker commented 9 years ago

What you're looking for is finally

rumkin commented 9 years ago

@felixfbecker finally or .finally()?

felixfbecker commented 9 years ago

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)

IdpugantiSanjay commented 4 years ago

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

rumkin commented 4 years ago

@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.