Если вы правильно связываете свои обещания, вы никогда не увидите это предупреждение, и все ваши ошибки будут обнаружены GraphQL. Предположим, у нас есть эти две функции, которые возвращают Promise, последняя из которых всегда отклоняет:
async function doSomething() {
return
}
async function alwaysReject() {
return Promise.reject(new Error('Oh no!'))
}
Сначала несколько правильных примеров:
someField: async () => {
await alwaysReject()
await doSomething()
},
// Or without async/await syntax
someField: () => {
return alwaysReject()
.then(() => {
return doSomething()
})
// or...
return alwaysReject().then(doSomething)
},
Во всех этих случаях вы увидите ошибку внутри массива errors
и никаких предупреждений в консоли. Мы могли бы изменить порядок функций (сначала вызвав doSomething
), и это все равно будет иметь место.
Теперь давайте разберем наш код:
someField: async () => {
alwaysReject()
await doSomething()
},
someField: () => {
alwaysReject() // <-- Note the missing return
.then(() => {
return doSomething()
})
},
В этих примерах мы запускаем функцию, но не ожидаем возвращенного обещания. Это означает, что выполнение нашего распознавателя продолжается. Если незапланированное Обещание разрешается, мы ничего не можем сделать с его результатом - если оно отклоняет, мы ничего не можем сделать с ошибкой (она не обработана, как указывает предупреждение).
В общем, вы всегда должны следить за тем, чтобы ваши Обещания были правильно связаны, как показано выше. Это значительно проще сделать с помощью синтаксиса async / await, поскольку без него очень легко пропустить return
.
А как насчет побочных эффектов?
Могут быть функции, которые возвращают Promise, который вы хотите запустить, но не хотите приостанавливать выполнение вашего распознавателя. Независимо от того, разрешается или возвращается Promise, не имеет значения, что возвращает ваш распознаватель, вам просто нужно запустить его. В этих случаях нам просто нужно catch
для обработки отклоняемого обещания:
someField: async () => {
alwaysReject()
.catch((error) => {
// Do something with the error
})
await doSomething()
},
Здесь мы вызываем alwaysReject
, и выполнение продолжается до doSomething
. Если alwaysReject
в итоге отклоняется, ошибка будет обнаружена, и в консоли не будет отображаться предупреждение.
Примечание: Эти "побочные эффекты" не ожидаются, что означает, что выполнение GraphQL будет продолжено и вполне может закончиться, пока они еще работают. Нет никакого способа включить ошибки от побочных эффектов в ваш ответ GraphQL (то есть массив errors
), в лучшем случае вы можете просто регистрировать их. Если вы хотите, чтобы в ответе была указана причина отклонения определенного Обещания, вам нужно подождать его в своем преобразователе, а не рассматривать его как побочный эффект.
Последнее слово о пробовать / ловить и ловить
При работе с Promises мы часто видим ошибки, обнаруженные после вызова нашей функции, например:
try {
await doSomething()
} catch (error) {
// handle error
}
return doSomething.catch((error) => {
//handle error
})
Это важно в синхронном контексте (например, при создании REST API с помощью Express). Невозможность поймать отклоненные обещания приведет к знакомому UnhandledPromiseRejectionWarning
. Тем не менее, поскольку уровень выполнения GraphQL эффективно функционирует как одна гигантская попытка / отлов, на самом деле нет необходимости отлавливать ваши ошибки, если ваши Обещания связаны / ожидаются должным образом. Это верно, если A) вы не имеете дело с побочными эффектами, как уже показано, или B) вы не хотите, чтобы ошибка не всплывала:
try {
// execution halts because we await
await alwaysReject()
catch (error) {
// error is caught, so execution will continue (unless I throw the error)
// because the resolver itself doesn't reject, the error won't be bubbled up
}
await doSomething()