This is not quite true with regards to promises - one can do
getY(x)
.then(returnedY => Promise.all([Promise.resolve(y), zPromise()])
.then([y, z] => { ...another side effect...; return Promise.all([Promise.resolve(y), Promise.resolve(z), sideEffectPromise()])
.then([y, z, _] => f(y, z));
Not the cleanest, but you preserve the isolation without relying on polluted variables bleeding into function scope. In addition, one can do proper error handling via .catch as opposed to the brute force unnecessary catchall that is a try-catch.