Можно ли сократить этот код, но чтобы он не лишался своих функций?
С помощью простой рекурсии будут вызываться функции sayHello и getAge до тех пор, пока наш пользователь не даст ответ:

const promt = (message = '') => window.prompt(message);

function sayHello() {
  let name = promt('Здравствуйте, как вас зовут?');
  return name || sayHello();
}

function getAge() {
  let age = promt('Сколько вам лет?');
  return age || getAge();
}

function greeting() {
	let name = sayHello();
  let age = getAge();

  alert(`Здравствуйте ${name}, вам ${age} лет.`);
}

greeting();

Если вам обязательно нужно задать второй вопрос, если пользователь не ответил на первый (который по умолчанию), то данную логику можно реализовать так:

const promt = (message = '') => window.prompt(message);

function getAnswerAnyway(defaultQuestion, repeatQuestion) {
  const iter = (atempts = 2) => {
    const question = atempts === 2 ? defaultQuestion : repeatQuestion;
    let age = promt(question);
    return age || iter(atempts - 1);
  }

  return iter(2);
}

function greeting() {
	let name = getAnswerAnyway(
    'Здравствуйте, как вас зовут?',
    'Я же спрашиваю, как вас зовут?',
  );

  let age = getAnswerAnyway(
    'Сколько вам лет?',
    'Почему молчите? Возраст укажите',
  );

  alert(`Здравствуйте ${name}, вам ${age} лет.`);
}

greeting();

Рабочий код можно посмотреть здесь.
У вас приличный код.  Код должен быть не короткий, а понятный. 
Как вариант, можно цикл чуть по другому определить. 
while (!userName) {
...
}

P.S.
userName == null  почитайте про разницу разницу между == и ===. В js это важно и обычно используют userName === null
Успехов!