javascript - 扁平化_very_嵌套密集型函数: how to?

标签 javascript node.js asynchronous

我是 JsonRestStores 的作者。我已经把这个问题推迟得太久了。这是一个将赢得“年度最愚蠢缩进函数”奖的函数。

主要的棘手问题是闭包变量在某个点发生了更改:

body[ self.idProperty ] = params[ self.idProperty ];

还有一个“如果”让事情变得有趣。

那么...有没有一种优雅的方法可以将此函数变成看起来不像带有两个戳的箭头的东西?如果是这样,您能提供一个示例实现吗?

  _makePostAppend: function( params, body, options, next ){

    var self = this;
    var body;

    if( typeof( next ) !== 'function' ) next = function(){};

    // Check that the method is implemented
    if( ! self.handlePostAppend ){
      self._sendError( next, new self.NotImplementedError( ) );
      return;
    }

    // Check the IDs
    self._checkParamIds( params, body, false, function( err ){  
      self._sendErrorOnErr( err, next, function(){

        self.schema.validate(  body, function( err, body, errors ) {
          self._sendErrorOnErr( err, next, function(){

            if( errors.length ){
              self._sendError( next, new self.UnprocessableEntityError( { errors: errors } ) );
            } else {

              // Fetch the doc
              self.execAllDbFetch( params, body, options, function( err, fullDoc ){
                self._sendErrorOnErr( err, next, function(){


                  self.extrapolateDoc( params, body, options, fullDoc, function( err, doc) {
                    self._sendErrorOnErr( err, next, function(){

                      self._castDoc( doc, function( err, doc) {
                        self._sendErrorOnErr( err, next, function(){

                          // Actually check permissions
                          self.checkPermissionsPostAppend( params, body, options, doc, fullDoc, function( err, granted ){
                            self._sendErrorOnErr( err, next, function(){

                              if( ! granted ){
                                self._sendError( next, new self.ForbiddenError() );
                              } else {

                                // Clean up body from things that are not to be submitted
                                //if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
                                self.schema.cleanup( body, 'doNotSave' );

                                // Paranoid check
                                // Make sure that the id property in the body does match
                                // the one passed as last parameter in the list of IDs
                                body[ self.idProperty ] = params[ self.idProperty ];

                                self.execPostDbAppend( params, body, options, doc, fullDoc, function( err, fullDocAfter ){
                                  self._sendErrorOnErr( err, next, function(){

                                    self.extrapolateDoc( params, body, options, fullDocAfter, function( err, doc) {
                                      self._sendErrorOnErr( err, next, function(){

                                        self._castDoc( fullDocAfter, function( err, docAfter) {
                                          self._sendErrorOnErr( err, next, function(){

                                            // Remote request: set headers, and send the doc back (if echo is on)
                                            if( self.remote ){
                                              if( self.echoAfterPostAppend ){

                                                 self.prepareBeforeSend( docAfter, function( err, docAfter ){
                                                   self._sendErrorOnErr( err, next, function(){

                                                      self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
                                                        self._sendErrorOnErr( err, next, function(){

                                                          self._res.json( 200, docAfter );

                                                        });
                                                      });
                                                   })
                                                 })
                                              } else { 

                                                self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
                                                  self._sendErrorOnErr( err, next, function(){

                                                    self._res.send( 204, '' );

                                                  });
                                                });

                                              }

                                            // Local request: simply return the doc to the asking function
                                            } else {

                                              self.prepareBeforeSend( docAfter, function( err, docAfter ){
                                                self._sendErrorOnErr( err, next, function(){

                                                  self.afterPostAppend( params, body, options, doc, fullDoc, docAfter, fullDocAfter, function( err ){
                                                    self._sendErrorOnErr( err, next, function(){

                                                      next( null, docAfter, self.idProperty );

                                                    })
                                                  })

                                                })
                                              })
                                            }

                                          })
                                        });

                                      });
                                    })


                                  }) // err
                                }) // execPostDbAppend

                              } // granted


                            })
                          }) 

                        }) 
                      }) 

                    })
                  }) 

                }) // err
              }) // checkPermissionsPostAppend

            } // errors.length

          }) // err
        }) // self.validate

      }) // err
    }) // self.validate
  },

最佳答案

如果我编写像您这样的代码,我更愿意使用 async图书馆,它是 waterfall函数,这样我就不必用它的 Promise 版本来包装异步 API。这非常简单。 promise 也很棒,@Esailija 的答案没有任何问题,但我个人认为这更容易实现,并且同样可读:

var async = require('async');

var _makePostAppend = function (params, body, options, next) {
  var self = this, body;
  if (typeof(next) !== 'function') next = function () { };

  // Check that the method is implemented
  if (!self.handlePostAppend) {
    self._sendError(next, new self.NotImplementedError());
    return;
  }

  async.waterfall([
    function (cb) {
      // Check the IDs
      self.checkParamIds(params, body, false, cb);
    },
    function (cb) {
      self.schema.validate(body, cb);
    },
    function (body, errors, cb) {
      if (errors.length) cb(new self.UnprocessableEntityError({ errors: errors }));
      // Fetch the doc
      self.execAllDbFetch(params, body, options, cb);
    },
    function (fullDoc, cb) {
      self.extrapolateDoc(params, body, options, fullDoc, function (err, doc) {
        cb(err, fullDoc, doc);
      });
    },
    function (fullDoc, doc, cb) {
      self._castDoc(doc, function (err, doc) {
        cb(err, fullDoc, doc);
      });
    },
    function (fullDoc, doc, cb) {
      // Actually check permissions
      self.checkPermissionsPostAppend(params, body, options, doc, fullDoc, function (err, granted) {
        cb(err, fullDoc, doc, granted);
      });
    },
    function (fullDoc, doc, granted, cb) {
      if (!granted) cb(new self.ForbiddenError());

      // Clean up body from things that are not to be submitted
      //if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
      self.schema.cleanup(body, 'doNotSave');

      // Paranoid check
      // Make sure that the id property in the body does match
      // the one passed as last parameter in the list of IDs
      body[self.idProperty] = params[self.idProperty];

      self.execPostDbAppend(params, body, options, doc, fullDoc, function (err, fullDocAfter) {
        cb(err, fullDoc, fullDocAfter);
      });
    },
    function (fullDoc, fullDocAfter, cb) {
      self.extrapolateDoc(params, body, options, fullDocAfter, function (err, doc) {
        cb(err, fullDoc, doc, fullDocAfter);
      });
    },
    function (fullDoc, doc, fullDocAfter, cb) {
      self._castDoc(fullDocAfter, function (err, docAfter) {
        cb(err, fullDoc, doc, fullDocAfter, docAfter);
      });
    }
  ], function (err, fullDoc, doc, fullDocAfter, docAfter) {
    self._sendErrorOnErr(err, next, function () {
      // Remote request: set headers, and send the doc back (if echo is on)
      if (self.remote) {
        if (self.echoAfterPostAppend) {
          async.waterfall([
            function (cb) {
              self.prepareBeforeSend(docAfter, cb);
            },
            function (docAfter, cb) {
              self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, cb)
            }
          ], function (err, docAfter) {
            self._sendErrorOnErr(err, next, function () {
                self._res.json(200, docAfter);
            });
          });
        } else {
          self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, function (err) {
            self._sendErrorOnErr(err, next, function () {
              self._res.send(204, '');
            });
          });
        }

        // Local request: simply return the doc to the asking function
      } else {
        async.waterfall([
          function (cb) {
            self.prepareBeforeSend(docAfter, function (err, docAfter) {
              cb(err, doc, fullDoc, fullDocAfter, docAfter);
            })
          },
          function (doc, fullDoc, fullDocAfter, docAfter, cb) {
            self.afterPostAppend(params, body, options, doc, fullDoc, docAfter, fullDocAfter, function (err) {
              cb(err, docAfter);
            });
          }
        ], function (err, docAfter) {
          self._sendErrorOnErr(err, next, function () {
            next(null, docAfter, self.idProperty);
          });
        });
      }
    });
  });
};

或者更好的是,我使用了 @Esailija 的答案中的范围界定技巧:

var async = require('async');

var _makePostAppend = function (params, body, options, next) {
  var _self = this, _body, _fullDoc, _doc, _docAfter, _fullDocAfter;
  if (typeof(next) !== 'function') next = function () { };

  // Check that the method is implemented
  if (!_self.handlePostAppend) {
    _self._sendError(next, new _self.NotImplementedError());
    return;
  }

  async.waterfall([
    function (cb) {
      // Check the IDs
      _self.checkParamIds(params, _body, false, cb);
    },
    function (cb) {
      _self.schema.validate(_body, cb);
    },
    function (body, errors, cb) {
      if (errors.length) cb(new _self.UnprocessableEntityError({ errors: errors }));
      // Fetch the doc
      _self.execAllDbFetch(params, body, options, cb);
    },
    function (fullDoc, cb) {
      _fullDoc = fullDoc;
      _self.extrapolateDoc(params, _body, options, fullDoc, db);
    },
    function (doc, cb) {
      _self._castDoc(doc, cb);
    },
    function (doc, cb) {
      _doc = doc;
      // Actually check permissions
      _self.checkPermissionsPostAppend(params, _body, options, doc, _fullDoc, cb);
    },
    function (granted, cb) {
      if (!granted) cb(new _self.ForbiddenError());

      // Clean up body from things that are not to be submitted
      //if( self.schema ) self.schema.cleanup( body, 'doNotSave' );
      _self.schema.cleanup(_body, 'doNotSave');

      // Paranoid check
      // Make sure that the id property in the body does match
      // the one passed as last parameter in the list of IDs
      _body[_self.idProperty] = params[_self.idProperty];

      _self.execPostDbAppend(params, _body, options, _doc, _fullDoc, cb);
    },
    function (fullDocAfter, cb) {
      _fullDocAfter = fullDocAfter;
      _self.extrapolateDoc(params, _body, options, fullDocAfter, cb);
    },
    function (doc, cb) {
      _doc = doc;
      _self._castDoc(_fullDocAfter, cb);
    }
  ], function (err, docAfter) {
    _self._sendErrorOnErr(err, next, function () {
      // Remote request: set headers, and send the doc back (if echo is on)
      if (_self.remote) {
        if (_self.echoAfterPostAppend) {
          async.waterfall([
            function (cb) {
              _self.prepareBeforeSend(docAfter, cb);
            },
            function (docAfter, cb) {
              _self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, cb)
            },
            function (cb) {
              _self._res.json(200, docAfter);
              cb();
            }
          ], function (err, results) {
            _self._sendErrorOnErr(err, next);
          });
        } else {
          _self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, function (err) {
            _self._sendErrorOnErr(err, next, function () {
              _self._res.send(204, '');
            });
          });
        }

        // Local request: simply return the doc to the asking function
      } else {
        async.waterfall([
          function (cb) {
            _self.prepareBeforeSend(docAfter, cb);
          },
          function (docAfter, cb) {
            _docAfter = docAfter;
            _self.afterPostAppend(params, _body, options, _doc, _fullDoc, docAfter, _fullDocAfter, cb);
          }
        ], function (err) {
          _self._sendErrorOnErr(err, next, function () {
            next(null, _docAfter, _self.idProperty);
          });
        });
      }
    });
  });
};

关于javascript - 扁平化_very_嵌套密集型函数: how to?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19874967/

相关文章:

javascript - Mocha 如何知道在测试套件中首先加载哪个文件

node.js - 浏览器和服务器之间实时模型同步的node.js框架有哪些?

javascript - 使用堆栈跟踪记录 “Warning” 而不是 “Error”

JavaScript 如何防止在 Promise 中推送 null?

c# - 如何在异步 ASP.NET Web 服务调用上定义客户端超时?

javascript - AngularJS:如何显示嵌套对象?

javascript - JQuery Ajax 只调用预定义函数

javascript - Promise 回调的触发顺序是什么?

asynchronous - 最终所有异步I/O都将在轮询中实现吗?

javascript - 向图像类添加类