JavaScript - Express JS: цепочка обещаний не работает / не останавливается при возвращении ключевого слова; - PullRequest
0 голосов
/ 30 октября 2018

У меня есть функция полезности в моем экспресс-приложении. По сути, я хочу либо продолжить, либо завершить цепочку обещаний then при определенных условиях. Но когда я хочу завершить цепочку с помощью оператора return, express говорит, что не может устанавливать заголовки после их отправки. Вот что у меня есть:

function submit_reset_password_request(request, response) {
  if(request.session.id) {
    return response.json({ error: true, message: 'password reset cannot be requested during an sctive session' });
  }

  let { email } = request.body;
  let user, reset_request;
  if(email) {
    email = email.toLowerCase().trim();
  }
  if(!email) {
    return response.json({ error: true, message: 'input is required' });
  }

  models.Users.findOne({ where: { email } })
  .then(user_result => {
    if(!user_result) {
      return response.json({ error: true, message: 'No account found by that email' });
    }
    user = user_result.dataValues;
    return models.ResetPasswordRequests.findOne({ where: { user_email: user.email } })
  })
  .then(request_result => {
    if(request_result) {
      return response.json({ error: true, message: 'A password reset has already been requested for this email' });
    }
    return models.ResetPasswordRequests.create({ user_email: user.email })
  })
  .then(new_reset_request => {
    reset_request = new_reset_request.dataValues;

    // send reset request email
    let host = request.get('host');
    let link = host.endsWith('/') ? (host + 'search') : (host + '/search');
    let email_subject = 'Epsity - Password reset requested';
    let email_html = templateEngine.PasswordReset_EMAIL({ user, reset_request, link });
    return sendgrid_manager.send_email(null, request.session.you.email, email_subject, email_html);
  })
  .then(email_result => {
    return response.json({ success: true, message: 'A password reset request has been sent to the provided email!' });
  })
  .catch(error => {
    console.log(error);
    return response.json({ error, message: 'Could not sumbit reset password request...' });
  });
}

Каждая цепочка then просматривает базу данных. операторы if должны остановить цепочку, отправить ответ и продолжить, если if ложно, но все равно продолжает (узел: я все еще вижу ответ на стороне клиента). Я не уверен почему.

Вот ошибка:

Unhandled rejection Error: Can't set headers after they are sent.
    at validateHeader (_http_outgoing.js:494:11)
    at ServerResponse.setHeader (_http_outgoing.js:501:3)
    at ServerResponse.header (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\express\lib\response.js:767:10)
    at ServerResponse.send (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\express\lib\response.js:170:12)
    at ServerResponse.json (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\express\lib\response.js:267:15)
    at models.Users.findOne.then.then.then.then.catch.error (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\server\routers\main\methods\post.js:221:21)
    at tryCatcher (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\util.js:16:23)
    at Promise._settlePromiseFromHandler (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\promise.js:512:31)
    at Promise._settlePromise (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\promise.js:569:18)
    at Promise._settlePromise0 (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\promise.js:614:10)
    at Promise._settlePromises (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\promise.js:690:18)
    at _drainQueueStep (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\async.js:138:12)
    at _drainQueue (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\async.js:131:9)
    at Async._drainQueues (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\async.js:147:5)
    at Immediate.Async.drainQueues (C:\Users\Waite-Ryan-M\Desktop\_my-apps\rmw-epsity\node_modules\bluebird\js\release\async.js:17:14)
    at runCallback (timers.js:789:20)
    at tryOnImmediate (timers.js:751:5)
    at processImmediate [as _immediateCallback] (timers.js:722:5)

Может быть, я попробую синтаксис async/await.

1 Ответ

0 голосов
/ 30 октября 2018

Проблема в том, что когда внутри .then внутри Promise цепочки, Promise не обязательно заботится о том, что вы возвращаете. В вашем

.then(user_result => {
  if(!user_result) {
    return response.json({ error: true, message: 'No account found by that email' });
  }
  user = user_result.dataValues;
  return models.ResetPasswordRequests.findOne({ where: { user_email: user.email } })
})

интерпретатор будет обрабатывать return response.json так же, как return models.Reset... - он просто будет передан следующему .then, ожидая разрешения Обещания, если выражение было Promise. Как вы видели, это не вырвется из цепи. Либо извлеките функции в именованные функции, которые вы можете вызывать, основываясь на потоке управления (который немного многословен), либо используйте async / await, что допускает нечто очень похожее на то, что вы делаете сейчас - с помощью async / await, return ing завершит функцию и, чтобы получить значение асинхронного вызова, используйте await (вместо return ing, чтобы следующий .then мог справиться с этим).

Как это:

async function submit_reset_password_request (request, response) {
  if(request.session.id) {
    return response.json({ error: true, message: 'password reset cannot be requested during an sctive session' });
  }
  const { email } = request.body;
  if(email) {
    email = email.toLowerCase().trim();
  }
  if(!email) {
    return response.json({ error: true, message: 'input is required' });
  }

  try {
    const user_result = await models.Users.findOne({ where: { email } });
    if(!user_result) {
      return response.json({ error: true, message: 'No account found by that email' });
    }
    const user = user_result.dataValues;
    const request_result = await models.ResetPasswordRequests.findOne({ where: { user_email: user.email } });
    if(request_result) {
      return response.json({ error: true, message: 'A password reset has already been requested for this email' });
    }
    const new_reset_request = await models.ResetPasswordRequests.create({ user_email: user.email });
    const reset_request = new_reset_request.dataValues;

    // send reset request email
    const host = request.get('host');
    const link = host.endsWith('/') ? (host + 'search') : (host + '/search');
    const email_subject = 'Epsity - Password reset requested';
    const email_html = templateEngine.PasswordReset_EMAIL({ user, reset_request, link });
    const email_result = await sendgrid_manager.send_email(null, request.session.you.email, email_subject, email_html);
    return response.json({ success: true, message: 'A password reset request has been sent to the provided email!' });
  } catch(error) {
    console.log(error);
    return response.json({ error, message: 'Could not sumbit reset password request...' });
  }
}
...