Disclaimer: this topic is really old and I learnt a few bits about coroutines since then, so today my answer would be probably a little different.
And you are correct. Even if we still await on the first deferred and the third fails, we still fail, because the failure automatically propagates to the parent. But this is only for the most typical case. If for example tasks are scheduled using another scope, then it won’t propagate to our coroutine. It may sound weird to do multiple anotherScope.async {} and immediately await on them, but imagine we have some kind of a service that schedules tasks and returns deferreds - in this case the exception will most propably not propagate to the caller. Supervisor scope or another similar functionality is another example. @al3c mentioned yet another important aspect that deferreds might be lazy.
I think the general meaning of awaitAll() should be something like: await concurrently on all deferreds. Going in a loop is awaiting sequentially, at least conceptually. Of course, usually if we have a collection of deferreds, that naturally means we run some tasks concurrently, so in practice the difference is often very small or there is no difference at all.