Instead of writing code like this, I encourage you to start writing your code to be “Promise-chain aware”. Yes, this is more code, but look how much it cleans up the promise chain. Additionally, logging the result object give you a much clearer picture of everything that happened during the event.

The general form of this pattern is the following. For each internal function, the input and the output are the same object. We continue to add more properties to the object as it passes through the promise chain.

functiongetUserFromCache(e){e.cacheKey=`users/${e.id}`;returncache.get(e.cacheKey).then(cacheHit=>{if(cacheHit){e.user=cacheHit;e.hasCacheHit=true;}else{e.hasCacheHit=false;}returne;});}functiongetUserFromDatabase(e){// We already have a user from cache.if(e.user){returne;}returndb.users.findOne({id:e.id}).then(user=>{e.user=user;returne;});}functiongetUserPermissionsFromDatabase(e){// We already have user permissions.if(e.user.permissions){returne;}returndb.permissions.findAll({userId:e.id}).then(permissions=>{e.user.permissions=permissions;returne;});}functiongetUserPropertiesFromDatabase(e){// We already have user properties.if(e.user.properties){returne;}returndb.userProperties.findAll({userId:e.id}).then(properties=>{e.user.properties=properties;returne;});}functionsaveUserToCache(e){// If we had a cache hit from before, don't save to catch again.if(e.hasCacheHit){returne;}returncache.set(e.cacheKey,e.user).then(()=>{returne;});}// Our public function now looks much simpler.functiongetUserDetails(id){returngetUserFromCache({id:id}).then(getUserFromDatabase).then(getUserPropertiesFromDatabase).then(getUserPermissionsFromDatabase).then(saveUserToCache)}

By the end of the promise chain, the result object should be a record of everything that happened.