@allObjects: are you sure you were looking at the right repository? :) waitReady is not called in any loop. It's only called at the end of writing operations (when flashing a page, when erasing a sector and when erasing the chip). That's it. As long as you only read, waitReady will not ever be called at all. But for writing it's neccessary, because otherwise you might get corrupt data:
Poll to check that erase/program are done. Unlike a read, programming can take tens of microseconds to milliseconds. A chip-level erase can take hundreds of milliseconds. If you execute an instruction before it is done, the erase does not finish! Write your memory drivers to poll the chip until it is done, even if it seems to be more complex and requires more CPU power.
(Source: The Positive Rail - Debugging the Winbond W25Q80BV)
The waitReady function itself is a loop. It could be optimized so that the polling happens in a setInterval function and not the main loop, so it becomes non-blocking and Espruino can do other stuff in the background. But then again, it adds a great deal of complexity, because your application would have to deal with callback functions and asynchronity. To be realistic, erasing the chip isn't something you do all the time, and flashing a page is fast enough to wait for it :)
It's most likely not related to my waveform issues, since in that application I am only reading (once I have flashed my test signal) and there's no ready-polling when reading.
Looked at the code in git... there is a lot of wait for ready, practically in every loop, even micro loop down to a byte ( EDIT: misread - see post #6) . I'not familiar enough with the chip and whether it could be done a differently - less frequently - to avoid that back and forth communication. I understand that structuring software interrupt / event driven adds complexity. But could that resolve some of the issues you're having with the waveforms?