避免在javascript中循环多个返回 – asynchronous/等待解决callback金字塔或callback地狱,
我有这个代码,有很多返回块,例如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
只是循环返回:
return() return() return()
来自PHP的这段代码看起来很疯狂,因为返回函数返回函数,而且对我来说还不清楚,javascript这个types的块的名字怎么返回代码呢? 调用函数,返回更多的代码?
更新:
代码不是我的,完整的源代码
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个返callback用函数和返回值和函数? 面向对象是从来没有这样,如何使用asynchronous/等待build议@dashmud,我不想学习像callback旧的东西
这个代码需要通过多种方式进行重构。 首先,你真的不想在相同的逻辑stream程中混合承诺和简单的asynchronouscallback。 它弄得一塌糊涂,破坏了许多承诺的优点。 然后,你在new Promise()
有一个使用new Promise()
的反模式。 然后,你需要更多的嵌套(你可以链接)。
以下是我的build议:
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; } }); } } } };
变更摘要:
- 最初收集所有错误,并在一个地方将
Promise.reject(err)
返回给所有这些初始错误。 - Promisify
encryptPassword()
所以你不是混合定期的callback与诺言逻辑stream程,然后你可以正确地返回和传播错误。 - 使用
return new Promise()
去除整个代码的包装,因为所有的asynchronous操作现在都被promisified,所以你可以直接返回promise。 这真的有助于正确的error handling。 - 撤消不必要的嵌套,而只是链。
- 删除
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()
。 - 它返回可能
undefined
的结果,所以所有return
操作是控制程序stream(退出包含函数),但是由于在同一级返回后没有代码,所以这是不必要的。 - 您将callback传递给
encryptPassword()
。 这个callback是asynchronous的,这意味着它稍后会被调用。 - callback得到两个参数:
err
和hash
。 在node.jsasynchronous调用约定中,如果第一个参数是truthy(例如包含一些真值),则表示错误。 如果它是falsey(通常为null
),那么没有错误,第二个参数包含asynchronous操作的结果。 - 如果发生错误,父承诺被拒绝,退出callback。 同样,从
reject
没有返回值,所以return
只是在这一点退出callback(所以callback中没有其他代码运行)。 - 然后,调用另一个asynchronous操作
User.create()
,并在其中设置一个新密码来传递args对象。 -
User.create()
返回一个承诺,以便使用User.create()
方法捕获结果.then()
并向其传递callback。 - 一段时间后,当asynchronous的
User.create()
完成时,它将解决它的承诺,这将导致调用.then()
callback,并将结果传递给它。 如果在该操作中有错误,则不会调用.catch()
callback,而是调用.catch()
callback。 在.catch()
callback中,父承诺被拒绝。 这是一种承诺反模式(在另一个承诺中解决父承诺),因为在正确的error handling中犯错是非常容易的。 我的答案显示如何避免这一点。
扩展@ jfriend00的答案 ,这里是一个使用ES2017 async
/ await
语法来平整callback金字塔或callback地狱的方法 ,但是您更喜欢调用它:
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
我select使用一个名为util.promisify()
的node.js内置转换函数,而不是手写我自己的Promisified promise-based util.promisify()
。
总的来说,还有一些改进可以做到,比如将signUp()
参数的validation移动到一个单独的函数中,但是没有一个与平坦的“callback地狱”反模式有关,这导致了基于promise的控制stream和asynchronous/等待语法。
首先是事情。 你的代码中没有循环。
如果你来自PHP,那么我猜想Javascript的asynchronous执行模式对你来说看起来很陌生。 我会build议学习更多关于Javascript。
按以下顺序学习以下内容:
- callback
- 承诺
- 发电机
- asynchronous/等待
这些是如何处理Javascript的asynchronous执行模型的不同方法,从最基本的(callback)到最现代的方法,“asynchronous/等待”。
asynchronous/等待将是最可读的,但是如果你了解asynchronous/等待的方式会更好,因为如果你不明白你在做什么,它们很容易被错误的使用。