Batches multiple async calls if they share the same rpc options.
Here is an example to explain what this class does.
Life of a key.get_async(options) API call:
*) Key gets the singleton Context instance and invokes Context.get.
*) Context.get calls Context._get_batcher.add(key, options). This
returns a future "fut" as the return value of key.get_async.
At this moment, key.get_async returns.
*) When more than "limit" number of _get_batcher.add() was called,
_get_batcher invokes its self._todo_tasklet, Context._get_tasklet,
with the list of keys seen so far.
*) Context._get_tasklet fires a MultiRPC and waits on it.
*) Upon MultiRPC completion, Context._get_tasklet passes on the results
to the respective "fut" from key.get_async.
*) If user calls "fut".get_result() before "limit" number of add() was called,
"fut".get_result() will repeatedly call eventloop.run1().
*) After processing immediate callbacks, eventloop will run idlers.
AutoBatcher._on_idle is an idler.
*) _on_idle will run the "todo_tasklet" before the batch is full.
So the engine is todo_tasklet, which is a proxy tasklet that can combine
arguments into batches and passes along results back to respective futures.
This class is mainly a helper that invokes todo_tasklet with the right
arguments at the right time.