Asynchronous programming allows starting a thread to assign a task without blocking the main thread. It uses resources optimally and avoids freezing user interfaces. Asynchronous programming evolved from threads to thread pools and futures. It differs from multithreading by staying mostly in a single thread without synchronization issues. Asynchronous tasks can be created using thread pools like fixed, cached, and scheduled pools. Completable futures improved on futures by making chaining of asynchronous tasks easier through completion stages that represent tasks and their states.