Несколько вещей, чтобы исправить здесь. Возврат Обещания (или любого другого значения) внутри обратного вызова ничего не делает, и это не позволит вам связать дополнительные Обещания так, как вы хотите. Вместо этого ваше обещание срабатывает в режиме обратного вызова и не ожидается.
Как правило, не смешивайте Обещания и обратные вызовы. Если вам абсолютно необходимо использовать обратные вызовы, всегда оборачивайте обратный вызов в Promise (как вы это делали в sendMail). К счастью, большинство популярных библиотек сегодня поддерживают как обратные вызовы, так и обещания. Вот как можно реорганизовать приведенный выше код для правильной цепочки всех ваших обещаний:
createDealer(_, params) {
return bcrypt.hash(params.dealer.password, 10) // note the return here!
.then(hash => {
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail@example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
return sendEmail(messageObj) // note the return here!
}).then(data => {
return session.run(query, params) // note the return here!
}).then(result => {
result.records[0].get("d").properties // note the return here!
})
bcrypt.hash
автоматически сгенерирует для вас соль, если вы не передадите одну - нет необходимости вызывать две отдельные функции
- Мы запускаем нашу цепочку обещаний с
bcrypt.hash
, поэтому нам нужно вернуть обещание, которое она возвращает. Средство распознавания должно возвращать значение или Promise, которое будет преобразовано в значение, в противном случае оно возвращает ноль.
- Внутри каждого
then
мы возвращаем Обещание. Таким образом, мы «связываем» наши Обещания, позволяя конечному значению, которое мы возвращаем в преобразователе, быть значением, к которому разрешается последнее Обещание в цепочке.
Нам также нужно исправить вашу функцию sendMail
, чтобы она действительно возвращала значение. Вы правильно возвращаете новое обещание внутри функции, но вам также нужно передать возвращенный объект data
для разрешения. Это говорит Обещанию разрешить это значение.
module.exports = (message) => new Promise((resolve, reject) => {
const data = // ...etc
mailgun.messages().send(data, (error) => {
if (error) reject(error) // no need to return, it's pointless
resolve(data) // pass data to resolve
})
})
Примечание: похоже на то, что официальная библиотека почтового оружия поддерживает Promises.
Кроме того, я настоятельно рекомендую вам использовать async / await, особенно при работе с длинной цепочкой Promise. Он менее подвержен ошибкам и более читабелен:
createDealer async (_, params) {
const hash = await bcrypt.hash(params.dealer.password)
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail@example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
const emailResult = await sendEmail(messageObj)
const result = await session.run(query, params)
return result.records[0].get("d").properties // still need to return!
}
РЕДАКТИРОВАТЬ: Что касается обнаружения ошибок, GraphQL будет отлавливать любые ошибки, выданные вашим распознавателем, что означает, что вы часто можете пропустить, используя catch
самостоятельно. Например, если ваш запрос к почтовому оружию завершится неудачно, он сгенерирует какую-то ошибку, и ваш запрос вернет ноль для data
и подробности ошибки внутри массива errors
.
Этого может быть достаточно, хотя 1) вы, возможно, захотите записать свой стек ошибок в другом месте; и 2) в процессе работы вы, вероятно, не хотите раскрывать подробности внутренних ошибок.
Это означает, что вы, вероятно, захотите использовать пользовательские ошибки. В качестве бонуса вы можете добавить некоторые собственные свойства к своим ошибкам, чтобы помочь клиенту красноречиво справиться с ними. Таким образом, ваш код может выглядеть примерно так:
class DeliveryFailureError extends Error {}
DeliveryFailureError.code = 'DELIVERY_FAILURE'
DeliveryFailureError.message = 'Sorry, we could not deliver the email to your account'
try {
await mailgun.messages.create()
} catch (err) {
logger.error('Mailgun request failed:', err.stack)
throw new DeliveryFailureError()
}