async-std tried to mirror rust's libstd, and tokio adapted it.
Similar the global `spawn` and the value returned from it work kinda the same +- some naming differences.
Even the `LocalExecutor`/`LocalSet` can be abstracted over to erase their differences.
That also true for all simple usages of timout.
The remaining differences are:
- different internal implementation details
- naming differences which are not that easy to abstract away
- some detail about time/timeout handling I forgot which for many use-cases doesn't matter
- the AsyncRead/AsyncWrite traits but this is going to get into std so this will go away soon
- some "advanced" features like the fact that tokio overlaps the non-blocking executor thread pool with a blocking worker thread pool, but that in many case not a very important feature.
- some differences in e.g. how to specify the number executor threads etc.
Most other differences are "old legacy left overs" from the pre-async/await futures time periode.
So while a generic in-language abstraction likely won't happen anytime soone one which uses feature flags and re-exports as basis should be very doable in the close future.
They also have the tendancy to get outdated and non-maintained, since most users will just go for a runtime directly.