I would say this looks a lot better in performance
function isPalindrome(str){ let isOk = true; for(let i=0; i < str.length/2 ; i++){ if(str[i] !== str[str.length - i -1]){ isOk = false; break;} } return isOk; } function generateAllPossibleSubstring(str = ''){ let result = new Set([]); for (let i = 0; i < str.length; i++) { for (let j = i + 1; j <= str.length; j++) { let sub = str.substring(i, j); if(!result.has(sub) && isPalindrome(sub)) result.add(sub); } } return result; } let res = generateAllPossibleSubstring('aabaa'); console.log(res); The isPalindrome function complexity is N/2 which is faster because we are not reversing then reconstructing the string by joining and then comparing the 2 strings.
Where as generateAllPossibleSubstring is skipping all the unnecessary iteration in your original code by generating only the possible substring of the given string. For lookup i am using set instead of includes function because set is much more faster and has constant lookup complexity.