Promise.resolve(value) returns a promise fulfilled with value; given a native promise it returns the same object, and given a thenable it follows it by calling then() in a later microtask. Promise.reject(reason) always returns a new rejected promise and never unwraps its argument.
const p = Promise.resolve(100);
console.log(Promise.resolve(p) === p); // true: no new wrapper
Promise.resolve({ then(onFulfilled) { onFulfilled("from a thenable"); } }).then(console.log);
Promise.reject(p).catch(reason => console.log("reject() does not unwrap:", reason === p));true reject() does not unwrap: true from a thenable
The thenable prints last because following it costs an extra microtask. A function that is sometimes asynchronous should always return a promise: return Promise.resolve(cached) on a cache hit, not the bare value.