避免在 javascript 中循环多个 returns - async / await 来解决回调金字塔或回调地狱,
avoid multiple returns looped in javascript - async / await to solve callback pyramid or callback hell,
我有这段代码,有很多 return 块,例如 SignUp()
connectors.js
const connectors = {
Auth: {
signUp(args) {
return new Promise((resolve, reject) => {
// Validate the data
if (!args.email) {
return reject({
code: 'email.empty',
message: 'Email is empty.'
});
} else if (!isEmail(args.email)) {
return reject({
code: 'email.invalid',
message: 'You have to provide a valid email.'
});
}
if (!args.password) {
return reject({
code: 'password.empty',
message: 'You have to provide a password.'
});
}
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
resolve(createToken({ id: user._id, email: user.email }));
})
.catch((err2) => {
if (err2.code === 11000) {
return reject({
code: 'user.exists',
message: 'There is already a user with this email.'
});
}
return reject(err2);
});
});
});
},
};
module.exports = connectors;
然后调用此代码的另一个代码:
const connectors = require('./connectors');
CallsignUp(root, args) {
const errors = [];
return connectors.Auth.signUp(args)
.then(token => ({
token,
errors
}))
.catch((err) => {
if (err.code && err.message) {
errors.push({
key: err.code,
value: err.message
});
return { token: null, errors };
}
throw new Error(err);
});
}
如何在 ES6 或 ES7 或 ES2017 中避免这种情况?
有:
return()
.then()
return()
.then
然后循环 returns:
return()
return()
return()
来自 PHP 这段代码看起来很疯狂,因为 return 的功能是 return 的功能,我不清楚,javascript 中的名称如何块类型 return 代码?再次调用 return 的函数更多代码?
更新:
代码不是我的,完整源代码在
https://github.com/jferrettiboke/react-auth-app-example
我想通过例子来理解:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
/src/utils/auth.js(这里是encryptPassword)
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt-nodejs');
const config = require('../config');
exports.encryptPassword = (password, callback) => {
// Generate a salt then run callback
bcrypt.genSalt(10, (err, salt) => {
if (err) { return callback(err); }
// Hash (encrypt) our password using the salt
return bcrypt.hash(password, salt, null, (err2, hash) => {
if (err2) { return callback(err2); }
return callback(null, hash);
});
});
};
有 3 个 return 调用函数和 returning 值和函数? OOP 从来不是这样的,@dashmud 建议如何使用 Async/Await,我不想学习像回调这样的老东西
要事第一。您的代码中没有循环。
如果您来自 PHP,那么我猜想 Javascript 的异步执行模型对您来说看起来很陌生。我建议多了解 Javascript.
按以下顺序学习:
- 回调
- 承诺
- 发电机
- Async/Await
这些是关于如何处理 Javascript 的异步执行模型的不同方法,从最基本的(回调)到最现代的方法 "async/await"。
Async/await 将是最具可读性的,但如果您了解 async/await 是如何形成的,那会更好,因为如果您不理解您的内容,它们很容易以错误的方式使用正在做。
此代码需要以多种方式进行重构。首先,您真的、真的不想在同一个逻辑流中混合使用 promises 和普通的异步回调。它弄得一团糟,破坏了 promise 的许多优点。然后,你有一个在 new Promise()
中使用 promises 的反模式。然后,您需要更多嵌套(您可以改为链接)。
这是我的建议:
function encryptPasswordPromise(pwd) {
return new Promise((resolve, reject) => {
encryptPassword(pwd, (err, hash) => {
err ? reject(new Error("The password could not be hashed.")) : resolve(hash);
});
});
}
const connectors = {
Auth: {
signUp(args) {
// Validate the data
let err;
if (!args.email) {
err = {code: 'email.empty', message: 'Email is empty.'};
} else if (!isEmail(args.email)) {
err = {code: 'email.invalid', message: 'You have to provide a valid email.'};
} else if (!args.password) {
err = {code: 'password.empty', message: 'You have to provide a password.'};
}
if (err) {
return Promise.reject(err);
} else {
return encryptPasswordPromise(args.password).then(hash => {
args.password = hash;
return User.create(args);
}).then((user) => {
return createToken({id: user._id, email: user.email});
}).catch(err2 => {
if (err2.code === 11000) {
throw new Error({code: 'user.exists', message: 'There is already a user with this email.'});
} else {
throw err2;
}
});
}
}
}
};
变更摘要:
- 最初收集所有错误,然后在一个地方对所有这些初始错误执行 return
Promise.reject(err)
。
- Promisify
encryptPassword()
这样您就不会将常规回调与 promise 逻辑流混合在一起,然后您可以正确地 return 并传播错误。
- 使用
return new Promise()
删除整个代码的包装,因为您的所有异步操作现在都已承诺,因此您可以直接 return 承诺。这也确实有助于正确处理错误。
- 取消不必要的嵌套,只用链代替。
- 删除
Object.assign()
,因为似乎没有理由这样做。
在您的编辑中,您要求解释此代码段:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
- 此代码调用
encryptPassword()
.
- 它 return 可能是
undefined
的结果,所以 return
所做的只是控制程序流(退出包含函数),但由于没有return 之后的代码在同一级别,这是不必要的。
- 您将回调传递给
encryptPassword()
。该回调是异步的,这意味着它会在一段时间后被调用。
- 回调有两个参数:
err
和 hash
。在 node.js 异步调用约定中,如果第一个参数是真值(例如包含一些真值),那么它表示一个错误。如果为假(通常为 null
),则没有错误,第二个参数包含异步操作的结果。
- 如果出现错误,父承诺将被拒绝并退出回调。同样,
reject
中没有 return 值,因此 return
仅用于在该点退出回调(因此回调中没有其他代码运行)。
- 然后,另一个异步操作
User.create()
被调用,它被传递给其中设置了新密码的 args 对象。
User.create()
return 是一个承诺,所以它的结果是用 .then()
方法捕获的,并传递给它的回调。
- 一段时间后,当异步
User.create()
完成时,它将解决其承诺,这将导致调用 .then()
回调并将结果传递给它。如果该操作出错,则不会调用 .then()
回调,而是调用 .catch()
回调。在 .catch()
回调中,父承诺被拒绝。这是一个 promise 反模式(在另一个 promise 中解析父 promise),因为在正确的错误处理中很容易出错。我的回答说明了如何避免这种情况。
扩展 , here's an approach that uses the ES2017 async
/ await
syntax to flatten the callback pyramid or callback hell,但您喜欢这样称呼它:
const encryptPasswordPromise = require('util').promisify(encryptPassword)
const connectors = {
Auth: {
async signUp (args) {
const { email, password } = args
// Validate the data
let err
if (!email) {
err = { code: 'email.empty', message: 'Email is empty.' }
} else if (!isEmail(email)) {
err = { code: 'email.invalid', message: 'You have to provide a valid email.' }
} else if (!password) {
err = { code: 'password.empty', message: 'You have to provide a password.' }
}
if (err) {
throw err
}
let hash
try {
hash = await encryptPasswordPromise(password)
} catch (err) {
throw new Error('The password could not be hashed.')
}
const { _id: id, email } = await User.create(Object.assign(args, { password: hash }))
try {
return createToken({ id, email })
} catch (err) {
if (err.code === 11000) {
throw { code: 'user.exists', message: 'There is already a user with this email.' }
} else {
throw err
}
}
}
}
}
module.exports = connectors
我没有手写我自己的基于 promise 的 promisified encryptPassword()
,我选择使用 node.js 内置转换函数来调用 util.promisify()
。
总的来说,仍然可以进行一些改进,例如将 signUp()
参数的验证移动到一个单独的函数,但是 none 其中与扁平化“回调地狱”有关" 产生基于承诺的控制流和 async/await 语法的反模式。
我有这段代码,有很多 return 块,例如 SignUp()
connectors.js
const connectors = {
Auth: {
signUp(args) {
return new Promise((resolve, reject) => {
// Validate the data
if (!args.email) {
return reject({
code: 'email.empty',
message: 'Email is empty.'
});
} else if (!isEmail(args.email)) {
return reject({
code: 'email.invalid',
message: 'You have to provide a valid email.'
});
}
if (!args.password) {
return reject({
code: 'password.empty',
message: 'You have to provide a password.'
});
}
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
resolve(createToken({ id: user._id, email: user.email }));
})
.catch((err2) => {
if (err2.code === 11000) {
return reject({
code: 'user.exists',
message: 'There is already a user with this email.'
});
}
return reject(err2);
});
});
});
},
};
module.exports = connectors;
然后调用此代码的另一个代码:
const connectors = require('./connectors');
CallsignUp(root, args) {
const errors = [];
return connectors.Auth.signUp(args)
.then(token => ({
token,
errors
}))
.catch((err) => {
if (err.code && err.message) {
errors.push({
key: err.code,
value: err.message
});
return { token: null, errors };
}
throw new Error(err);
});
}
如何在 ES6 或 ES7 或 ES2017 中避免这种情况?
有:
return()
.then()
return()
.then
然后循环 returns:
return()
return()
return()
来自 PHP 这段代码看起来很疯狂,因为 return 的功能是 return 的功能,我不清楚,javascript 中的名称如何块类型 return 代码?再次调用 return 的函数更多代码?
更新:
代码不是我的,完整源代码在
https://github.com/jferrettiboke/react-auth-app-example
我想通过例子来理解:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
/src/utils/auth.js(这里是encryptPassword)
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt-nodejs');
const config = require('../config');
exports.encryptPassword = (password, callback) => {
// Generate a salt then run callback
bcrypt.genSalt(10, (err, salt) => {
if (err) { return callback(err); }
// Hash (encrypt) our password using the salt
return bcrypt.hash(password, salt, null, (err2, hash) => {
if (err2) { return callback(err2); }
return callback(null, hash);
});
});
};
有 3 个 return 调用函数和 returning 值和函数? OOP 从来不是这样的,@dashmud 建议如何使用 Async/Await,我不想学习像回调这样的老东西
要事第一。您的代码中没有循环。
如果您来自 PHP,那么我猜想 Javascript 的异步执行模型对您来说看起来很陌生。我建议多了解 Javascript.
按以下顺序学习:
- 回调
- 承诺
- 发电机
- Async/Await
这些是关于如何处理 Javascript 的异步执行模型的不同方法,从最基本的(回调)到最现代的方法 "async/await"。
Async/await 将是最具可读性的,但如果您了解 async/await 是如何形成的,那会更好,因为如果您不理解您的内容,它们很容易以错误的方式使用正在做。
此代码需要以多种方式进行重构。首先,您真的、真的不想在同一个逻辑流中混合使用 promises 和普通的异步回调。它弄得一团糟,破坏了 promise 的许多优点。然后,你有一个在 new Promise()
中使用 promises 的反模式。然后,您需要更多嵌套(您可以改为链接)。
这是我的建议:
function encryptPasswordPromise(pwd) {
return new Promise((resolve, reject) => {
encryptPassword(pwd, (err, hash) => {
err ? reject(new Error("The password could not be hashed.")) : resolve(hash);
});
});
}
const connectors = {
Auth: {
signUp(args) {
// Validate the data
let err;
if (!args.email) {
err = {code: 'email.empty', message: 'Email is empty.'};
} else if (!isEmail(args.email)) {
err = {code: 'email.invalid', message: 'You have to provide a valid email.'};
} else if (!args.password) {
err = {code: 'password.empty', message: 'You have to provide a password.'};
}
if (err) {
return Promise.reject(err);
} else {
return encryptPasswordPromise(args.password).then(hash => {
args.password = hash;
return User.create(args);
}).then((user) => {
return createToken({id: user._id, email: user.email});
}).catch(err2 => {
if (err2.code === 11000) {
throw new Error({code: 'user.exists', message: 'There is already a user with this email.'});
} else {
throw err2;
}
});
}
}
}
};
变更摘要:
- 最初收集所有错误,然后在一个地方对所有这些初始错误执行 return
Promise.reject(err)
。 - Promisify
encryptPassword()
这样您就不会将常规回调与 promise 逻辑流混合在一起,然后您可以正确地 return 并传播错误。 - 使用
return new Promise()
删除整个代码的包装,因为您的所有异步操作现在都已承诺,因此您可以直接 return 承诺。这也确实有助于正确处理错误。 - 取消不必要的嵌套,只用链代替。
- 删除
Object.assign()
,因为似乎没有理由这样做。
在您的编辑中,您要求解释此代码段:
return encryptPassword(args.password, (err, hash) => {
if (err) {
return reject(new Error('The password could not be hashed.'));
}
return User.create(Object.assign(args, { password: hash }))
.then((user) => {
.catch((err2) => {
return reject(err2);
- 此代码调用
encryptPassword()
. - 它 return 可能是
undefined
的结果,所以return
所做的只是控制程序流(退出包含函数),但由于没有return 之后的代码在同一级别,这是不必要的。 - 您将回调传递给
encryptPassword()
。该回调是异步的,这意味着它会在一段时间后被调用。 - 回调有两个参数:
err
和hash
。在 node.js 异步调用约定中,如果第一个参数是真值(例如包含一些真值),那么它表示一个错误。如果为假(通常为null
),则没有错误,第二个参数包含异步操作的结果。 - 如果出现错误,父承诺将被拒绝并退出回调。同样,
reject
中没有 return 值,因此return
仅用于在该点退出回调(因此回调中没有其他代码运行)。 - 然后,另一个异步操作
User.create()
被调用,它被传递给其中设置了新密码的 args 对象。 User.create()
return 是一个承诺,所以它的结果是用.then()
方法捕获的,并传递给它的回调。- 一段时间后,当异步
User.create()
完成时,它将解决其承诺,这将导致调用.then()
回调并将结果传递给它。如果该操作出错,则不会调用.then()
回调,而是调用.catch()
回调。在.catch()
回调中,父承诺被拒绝。这是一个 promise 反模式(在另一个 promise 中解析父 promise),因为在正确的错误处理中很容易出错。我的回答说明了如何避免这种情况。
扩展 async
/ await
syntax to flatten the callback pyramid or callback hell,但您喜欢这样称呼它:
const encryptPasswordPromise = require('util').promisify(encryptPassword)
const connectors = {
Auth: {
async signUp (args) {
const { email, password } = args
// Validate the data
let err
if (!email) {
err = { code: 'email.empty', message: 'Email is empty.' }
} else if (!isEmail(email)) {
err = { code: 'email.invalid', message: 'You have to provide a valid email.' }
} else if (!password) {
err = { code: 'password.empty', message: 'You have to provide a password.' }
}
if (err) {
throw err
}
let hash
try {
hash = await encryptPasswordPromise(password)
} catch (err) {
throw new Error('The password could not be hashed.')
}
const { _id: id, email } = await User.create(Object.assign(args, { password: hash }))
try {
return createToken({ id, email })
} catch (err) {
if (err.code === 11000) {
throw { code: 'user.exists', message: 'There is already a user with this email.' }
} else {
throw err
}
}
}
}
}
module.exports = connectors
我没有手写我自己的基于 promise 的 promisified encryptPassword()
,我选择使用 node.js 内置转换函数来调用 util.promisify()
。
总的来说,仍然可以进行一些改进,例如将 signUp()
参数的验证移动到一个单独的函数,但是 none 其中与扁平化“回调地狱”有关" 产生基于承诺的控制流和 async/await 语法的反模式。