Итак, у меня есть приложение Express, которое использует промежуточное программное обеспечение для анализа запросов JSON POST и заполнения объекта req.body
. Затем у меня есть цепочка обещаний, которая проверяет данные по схеме с помощью Joi, а затем сохраняет их в базе данных.
Я хотел бы проверить, возникла ли ошибка после одного из этих процессов, обработать ее соответствующим образом, отправив код состояния, затем ПОЛНОСТЬЮ ОТМЕНИТЬ цепочку обещаний. Я чувствую, что должен быть какой-то ЧРЕЗВЫЧАЙНО ЧИСТЫЙ И ПРОСТОЙ способ сделать это (возможно, какое-то заявление о разрыве?), Но я нигде не могу его найти. Вот мой код. Я оставил комментарии, показывающие, где я надеюсь прервать цепочку обещаний.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
4 ответа
Вы не можете прервать цепочку обещаний в середине. Он будет вызывать .then()
или .catch()
позже в цепочке (при условии, что есть и то, и другое, и при условии, что ваши обещания разрешены или отклонены).
Обычно, как вы справляетесь с этим, вы помещаете один .catch()
в конец цепочки, и он проверяет тип ошибки и предпринимает соответствующие действия. Вы не обрабатываете ошибку ранее в цепочке. Вы позволяете последним .catch()
обрабатывать вещи.
Вот что я бы посоветовал:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
Это дает несколько преимуществ:
- Любая ошибка распространяется на последний
.catch()
в конце цепочки, где она регистрируется, и соответствующий код отправляется только в одном месте кода. - Успех обрабатывается только в одном месте, где этот статус отправляется.
- Это бесконечно расширяемо для большего количества звеньев в цепочке. Если у вас есть больше операций, которые могут иметь ошибки, они могут «прервать» остальную часть цепочки (кроме последней
.catch()
, просто отклонив ее с соответствующим объектом ошибки). - Это несколько похоже на практику разработки, заключающуюся в том, чтобы не иметь много выражений
return value
по всей вашей функции, а скорее накапливать результат и затем возвращать его в конце, что некоторые люди считают хорошей практикой для сложной функции. - При отладке вы можете установить точки останова в одной
.then()
и одной.catch()
, чтобы увидеть окончательное разрешение цепочки обещаний, поскольку вся цепочка проходит либо через последний.then()
, либо последний {{X3} } .
Одна стратегия состоит в том, чтобы отделить обработку ошибок в подпроцессах, которые имеют свою индивидуальную обработку ошибок. Если вы выбросите из них ошибку, вы обойдете основную цепочку обещаний.
Что-то типа:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
Вы можете пропустить упаковку Promise.resolve().then()
, но она включена в иллюстративных целях общего шаблона разделения каждой задачи и обработки ошибок.
.catch
возвращает разрешенное обещание по умолчанию. Вы хотите отклоненный Promsise . Итак, вы должны return
отклонить обещание изнутри .catch
, чтобы будущие .then
не выполнялись:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
Но обратите внимание, что это приведет к предупреждению консоли:
Uncaught (в обещании) ...
Поэтому было бы неплохо добавить еще один .catch
в конец, чтобы подавить ошибку (а также перехватить любые другие ошибки, которые появляются):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
Если вы хотите избежать всех будущих .then
s и future .catch
es, я полагаю, вы могли бы вернуть Promise
, который никогда не разрешается, хотя на самом деле это не так звучит как знак хорошо разработанной кодовой базы:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
Более чистое решение, которое вы пытаетесь выполнить, может заключаться в использовании экспресс-проверки, это простая оболочка для joi, которая предоставляет вам express middleware для проверки тела, параметров, запросов, заголовков и файлов cookie экспресс-запроса на основе вашей схемы Joi.
Таким образом, вы можете просто обрабатывать любые ошибки проверки Joi, возникающие в промежуточном программном обеспечении в вашем «универсальном» экспрессе обработчик ошибок, с чем-то вроде:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
Если вы не хотите использовать пакет экспресс-проверки, вы можете написать свой собственное простое промежуточное ПО, которое выполняет более или менее то же самое, как описано здесь (см. пример здесь).
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.