2017-07-14 67 views
0

我有一個Node 8/Express 4/Mongoose 4 API,並希望推廣一些代碼,以便我可以將其重用於其他部分。承諾鏈與簡單性

考慮下面的代碼,將創建一個新用戶:

function postUser(req, res, next) { 
    var body = req.body; 
    if ("data" in body) { 
    var user = new User(body.data); 
    user.save(function(err, savedUser) { 
     if (err) { 
     if (err.name === 'MongoError' && err.code === 11000) { 
      // user already exists 
      res.status(400).json({status: "fail", message: "User already exists"}); 
     } else { 
      return next(err); 
     } 
     } else { 
     // user successfully saved 
     res.json({status: "success", data: savedUser}); 
     } 
    }); 
    } else { 
    // malformed body 
    res.status(400).json({status: "fail", message: "Malformed body"}); 
    } 
} 

讓我們假設我有其他的功能,會做類似的工作,他們中的一些回調地獄。我如何最好地概括上述代碼?我想過用諾言鏈是這樣的:

function postUser(req, res, next) { 
    validateBody(req.body) 
    .then(createNewUser) 
    .then(user => sendUser(user, res)) 
    .catch(e => handleErrors(e, res)); 
} 

function validateBody(body) { 
    return new Promise(function(resolve, reject) { 
    if ("data" in body) { 
     resolve(body.data); 
    } else { 
     reject(new InvalidBodyError()); 
    } 
    }); 
} 

function createNewUser(userObj) { 
    return new Promise(function(resolve, reject) { 
    var user = new User(userObj); 
    user.save(function(err, savedUser) { 
     if (err) { 
     if (err.name === 'MongoError' && err.code === 11000) { 
      // user already exists 
      reject(new UserAlreadyExistsError(userObj)); 
     } else { 
      // other error 
      reject(err); 
     } 
     } else { 
     // user successfully saved 
     resolve(savedUser); 
     } 
    }); 
    }); 
} 

function handleErrors(e, res) { 
    if (e instanceof InvalidObjectIdError) handleInvalidObjectIdError(e, res) 
    else if (e instanceof UserNotFoundError) handleUserNotFoundError(e, res) 
    else if (e instanceof InvalidBodyError) handleInvalidBodyError(e, res) 
    else if (e instanceof UserAlreadyExistsError) handleUserAlreadyExistsError(e, res) 
    // TODO: handle unknown errors 
} 

正如你所看到的,它看起來更清潔,更可重複使用。但如何在負載下運行?我特別關心爲每個請求創建多個承諾。這是否規模?

解決這將是創建一個能夠解決通用的東西,然後擴展這個類實現特定的方法(僞)的通用基礎類的另一種方式:

class Action { 
    constructor() {} 

    postDoc(Base, req, res, next) { 
    var body = req.body; 
    if ("data" in body) { 
     var doc= new Base(body.data); 
     doc.save(function(err, savedDoc) { 
     if (err) { 
      if (err.name === 'MongoError' && err.code === 11000) { 
      // docalready exists 
      res.status(400).json({status: "fail", message: "Doc already exists"}); 
      } else { 
      return next(err); 
      } 
     } else { 
      // user successfully saved 
      res.json({status: "success", data: savedDoc}); 
     } 
     }); 
    } else { 
     // malformed body 
     res.status(400).json({status: "fail", message: "Malformed body"}); 
    } 
    } 
} 

class UserAction extends Action { 
    constructor() { 
     super(); 
    } 

    postUser(body, req, res, next) { 
     this.postDoc(User, req, res, next); 
    } 
} 

class AnotherAction extends Action { 
    constructor() { 
     super(); 
    } 

    postAnother(body, req, res, next) { 
     this.postDoc(AnotherBase, req, res, next); 
    } 
} 

,然後只用UserAction或AnotherAction(用戶是我的情況下的貓鼬模型)。 你更喜歡哪一個?

回答

0

我想過使用這樣的承諾鏈,它更乾淨,更可重用。但如何在負載下運行?

就好。

我特別關心爲每個請求創建多個承諾。這是否規模?

是的。承諾很便宜。看看您爲每個請求創建了多少個其他對象和回調閉包 - 它的縮放比例完全相同。

但是,可以進一步簡化:

function validateBody(body) { 
    return "data" in body 
    ? Promise.resolve(body.data) 
    : Promise.reject(new InvalidBodyError()); 
} 

function createNewUser(userObj) { 
    return new Promise(function(resolve, reject) { 
    new User(userObj).save(function(err, savedUser) { 
     if (err) reject(err); 
     else resolve(savedUser); 
    }); 
    }).catch((err) => { 
    if (err.name === 'MongoError' && err.code === 11000) { 
     // user already exists 
     throw new UserAlreadyExistsError(userObj); 
    } else { 
     // other error 
     throw err; 
    }); 
    }); 
} 

解決這將是創建一個能夠解決通用的東西,然後用具體實現的方法

擴展這一類的通用基礎類的另一種方式

不,不這樣做。繼承在這裏是錯誤的工具。創建像postDoc這樣的通用助手函數,它抽象出要創建的文檔類型,這是一個好主意,但沒有理由將它們放在class es中。將它們與承諾結合起來。如果輔助函數的參數數量不受控制,則可以使用對象,甚至可以使用對象,但不要使用繼承 - 而是使用不同的實例。例如,代碼可能如下所示:

const userAction = new Action(User, UserAlreadyExistsError); 
const anotherAction = new Action(AnotherBase, …); 

function post(action) { 
    return (req, res, next) => { 
    action.postDoc(req) 
    .then(doc => send(doc, res)) 
    .catch(e => handleErrors(e, res)); 
    }; 
} 
+0

太棒了,謝謝! – 1FpGLLjZSZMx6k