A PoW has at least the redeeming feature of consuming some resources for those 3s (or so...) making it self-limiting; there's only so many such computations a computer can do in parallel.
It consumes the most valuable resource in the world --- time.
A 3 second form delay just means the difference between a spammer launching 1000000 requests and posting them immediately to your database, vs them launching 1000000 requests and posting them to your database 3 seconds later.
My servers don't have enough bandwidth for that. Most of the connections are going to get dropped one way or the other. In my case, they will be intentionally dropped as being a likely denial of service attack.
You're sending two requests either way, it doesn't matter that you're waiting 3s because you can just do it in parallel.
A little math for you:
An 8 char password using only letters and numbers has roughly 1 x 10^14 permutations. Just for the sake of argument, let's assume that your server and your service provider can actually handle 1000000 simultaneous, parallel requests from 1000000 different IPs.
It can't unless you're Google or Facebook running your own data centers but for the sake of argument let's just ignore that reality and push on.
To check every possible 8 character password by making 1 million parallel attempts at guessing the password every 3 seconds would take roughly 10 years. Luck being what it is, you'd probably only need to check half of them ... but that would still take 5 years.
Back in reality land, you'll be out of business before 5 years because you can't serve your paying customers. 1000000 parallel requests hitting a run of the mill server is effectively a "denial of service" attack.
In reality land using servers that I run, your 1000000 different IPs would all be banned after about 30 seconds.
Not really --- only 8 bytes per token and they can be discarded on successful login. Tokens older than X minutes or with more than X attempts can be discarded/rejected too.
How many users are legitimately attempting to log in to your server at the same time?
If you worried about this, encode the current time into the token using a hashing/encryption/checksum method of your choice. This way, everything needed to validate the attempt is submitted along with the credentials.
1) Include a hidden, unique token when the login screen is served to client.
2) On the client, enforce a minimum 3 sec delay from time of screen load before the login will be submitted with the hidden token included.
3) On the server, if the hidden token isn't returned or is unknown/not found or if the delay from the time of issue is less than 3 sec., then reject the login and optionally ban the IP address after too many failed attempts.
The 3 sec delay is more than enough to discourage/prevent brute force attacks but not enough to annoy legitimate users.