2

所以,代码有效,但缩进级别太疯狂了......节点中的所有回调,我应该如何编码?

"use strict";

var crypto = require('crypto'),
    fs     = require('fs'),
    mmm    = require('mmmagic'),
    Magic  = require('mmmagic').Magic,
    path   = require('path');

console.log('Init controller: ' + path.basename(__filename));

exports.help = function () {
    var help;

    help  = "POST http://server/images\n";
    help += "    Upload image for storage.\n";
    help += "    <image> - The image file to upload\n";
    help += "    <title> - The title of the image, no more than 50 characters\n";
    help += "    <desc>  - The description of the image, no more than 1024 characters\n";

    return help;
}

exports.post = function (req, res) {
    var image = req.files.image;
    if (typeof(image) == 'undefined') {
        res.status(400).send("{error:'Upload error'}");
        return;
    }

    var magic = new Magic(mmm.MAGIC_MIME_TYPE);
    magic.detectFile(image.path, function(err, result) {
        if (err) {
            res.status(400).send("{error:'Upload mime error'}");
        } else {
            var mime = result.toLowerCase().split('/');

            if (mime[0] != 'image') {
                res.status(400).send("{error:'Upload not an image', mime: '" + result + "'}");
            } else {
                // Read the image file
                fs.readFile(image.path, function (err, data) {
                    if (err) {
                        res.status(400).send("{error:'Upload read error'}");
                    } else {
                        var hash = crypto.createHash('md5').update(data).digest("hex");
                        req.app.models.image.count({'hash': hash}, function (err, count) {
                            if (err) {
                                res.status(400).send("{error:'ORM Error: '" + JSON.stringify(err) + "'}");
                            } else {
                                if (count > 0) {
                                    res.status(400).send("{error:'Image already exists'}");
                                } else {
                                    var hash = crypto.createHash('md5').update(data).digest("hex");
                                    var newPath = path.join(req.app.tempDir, hash);
                                    fs.writeFile(newPath, data, function (err) {
                                        if (err) {
                                            res.status(400).send("{error:'Upload write error'}");
                                        } else {
                                            // Save the image
                                            req.app.models.image.create([{
                                                'hash'      : hash,
                                                'mime'      : mime,
                                                title       : '',
                                                description : ''
                                            }], function(err, images) {
                                                if (err) {
                                                    fs.unlink(newPath);
                                                    res.status(400).send("{error:'" + err.message + "'}");
                                                } else {
                                                    res.status(200).send("{id:'" + images[0].id + "}");
                                                }
                                            });
                                        }
                                    });
                                }
                            }
                        });
                    }
                });
            }
        }
    });
}
4

6 回答 6

5

http://callbackhell.com/有一个适应异步编程的指南。

评论中的一些后续行动:

  • 如果使异步代码看起来像自上而下的同步代码对您有吸引力并且被这种错觉误导不会让您非常紧张,那么有诸如Node FibersIced CoffeeScript之类的项目。但是,我强烈建议使用常规的 javascript 和带有回调(而不是承诺)的异步编程,直到灯泡亮起,然后再探索您并不真正理解的“问题”的解决方案。
  • 异步代码是在操作系统完成 I/O 时执行的一堆代码。这就是它不自顶向下读取的原因——因为它不自顶向下执行。它在 IO 完成时执行,这就是它以这种方式扩展的原因。
  • 不要被初学者的片段误导。在确定异步代码不可读之前,请先查看 TJ Holowaychuk 等大师的代码。回调地狱是一种初学者现象。这是非常普遍的。几乎每个人都将其作为一个阶段进行,但要超越它并不是特别困难,尤其是考虑到掌握多线程锁定和信号量及其调试的替代方法。可读性好的异步代码通常只是周围设计得更好的直接代码。但是,是的,它缺乏同步代码的自上而下的有序性,这意味着更多地在源代码中跳转。
于 2013-09-09T05:48:23.397 回答
2

<shamelessPlug>

我经历了回调意大利面的过程--> 承诺意大利面--> 写了自己的承诺管理库

该库可在此处获得

...这更详细地描述了上述过程

</shamelessPlug>

无论如何,一般的答案是肯定的——你最终会得到一堆回调。为了阻止这种情况发生,最好使用回调管理系统或承诺。

哪种方法最好?这真的取决于你,以及你喜欢什么。

于 2013-09-09T06:08:35.470 回答
1

我发现 Promise 的概念在这里很有帮助。在 JavaScript 中有很多 Promise 的实现,但是思路是一样的。你可以从这个开始:

https://github.com/kriszyp/node-promise

于 2013-09-09T05:32:14.767 回答
1

来一个家伙。你真的认为你需要一个图书馆吗?您可以使用纯 javascript 处理它。下面是重写的代码:

var response = null,
    request = null;
var fileDetected = function(err, result) {
    if (err) {
        response.status(400).send("{error:'Upload mime error'}");
    } else {
        var mime = result.toLowerCase().split('/');
        if (mime[0] != 'image') {
            response.status(400).send("{error:'Upload not an image', mime: '" + result + "'}");
        } else {
            // Read the image file
            fs.readFile(image.path, onReadFile);
        }
    }
}
var onReadFile = function(err, data) {
    if (err) {
        response.status(400).send("{error:'Upload read error'}");
    } else {
        var hash = crypto.createHash('md5').update(data).digest("hex");
        request.app.models.image.count({'hash': hash}, onImageCount);
    }
}
var onImageCount = function(err, count) {
    if (err) {
        response.status(400).send("{error:'ORM Error: '" + JSON.stringify(err) + "'}");
    } else {
        if (count > 0) {
            response.status(400).send("{error:'Image already exists'}");
        } else {
            var hash = crypto.createHash('md5').update(data).digest("hex");
            var newPath = path.join(request.app.tempDir, hash);
            fs.writeFile(newPath, data, onFileWrite);
        }
    }
}
var onFileWrite = function(err) {
    if (err) {
        response.status(400).send("{error:'Upload write error'}");
    } else {
        // Save the image
        request.app.models.image.create([{
            'hash'      : hash,
            'mime'      : mime,
            title       : '',
            description : ''
        }], function(err, images) {
            if (err) {
                fs.unlink(newPath);
                response.status(400).send("{error:'" + err.message + "'}");
            } else {
                response.status(200).send("{id:'" + images[0].id + "}");
            }
        });
    }
}

exports.post = function (req, res) {
    request = req;
    response = res;
    var image = request.files.image;
    if (typeof(image) == 'undefined') {
        response.status(400).send("{error:'Upload error'}");
        return;
    }
    var magic = new Magic(mmm.MAGIC_MIME_TYPE);
    magic.detectFile(image.path, fileDetected);
}

好的部分是,通过将不同功能中的所有内容分开,您实际上将逻辑拆分为块。新创建的函数的名称说明了块的用途。

于 2013-09-09T07:37:44.810 回答
0

如果您需要评论某些内容是“加载图像”或“保存图像”,将它们提取到单独的函数中将有助于提高可读性、降低缩进级别并消除对评论的需要。

即代替写作

// ... 
      petAHorse(function(horsesResponse) {
        // ...
      })
// ... 

你可以写

function horsePettedHandler(horsesResponse) {
  // ...
}

// ... 
      petAHorse(horsePettedHandler);
// ...
于 2013-09-09T05:15:45.093 回答
0

我更喜欢使用node-sync lib而不是 promises 。因为它允许一件好事:您不必为 promise 包装异步库函数,您可以使用特殊语法来使用它。像这样:

var result = thirdPartyLibAsyncFunction.sync(null, 2, 3);

它以相同的方式与您自己的代码一起工作。

于 2013-09-09T07:47:53.547 回答