2013-09-09 2 views
2

Итак, код работает, но уровень отступа безумен ... Со всеми обратными вызовами в узле, как именно я должен кодироваться?Новое для node.js, и я не уверен, что я делаю это правильно

"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 + "}"); 
               } 
              }); 
             } 
            }); 
           } 
          } 
         }); 
        } 
       }); 
      } 
     } 
    }); 
} 
+0

Многие люди будут рекомендовать различные библиотеки, чтобы помочь справиться с асинхронным кодом; Я перехожу через один из них, называемый async, который может оказаться полезным: http://nodecasts.net/episodes/5-thinking-asynchronously (управление обратным вызовом начинается примерно в 3:54) –

ответ

5

http://callbackhell.com/ имеет руководство по настройке для асинхронного программирования.

Некоторые последующие мероприятия из комментариев:

  • Есть проекты, такие как Node Fibers и Iced CoffeeScript если делает асинхронный код выглядеть сверху вниз синхронное кода обращается к вам и будучи введены в заблуждение, что иллюзия не вы очень нервничаете. Тем не менее, я настоятельно рекомендую работать с обычным javascript и асинхронным программированием с обратными вызовами (не обещаниями) до тех пор, пока лампочка не загорится, прежде чем исследовать решения «проблемы», которые вы действительно не понимаете.
  • Асинхронный код - это куча бит кода, который выполняется, когда ОС выполняется с помощью ввода-вывода. Вот почему он не читает сверху вниз - потому что он не выполняет сверху вниз. Он выполняется всякий раз, когда выполняется IO, поэтому он масштабируется так, как он делает.
  • Не вводите в заблуждение новичками. Посмотрите на код мастеров, таких как TJ Holowaychuk, прежде чем решить, что асинхронный код не читается. Обратный ад - явление новичка. Это чрезвычайно распространено. Почти каждый проходит через это как фазу, но не особенно сложно выйти за пределы, особенно учитывая альтернативу овладения многопоточными блокировками и семафорами и их отладкой. Асинхронный код, который хорошо читается, - это просто простой код с улучшенным дизайном. Но да, ему не хватает упорядоченного порядка синхронного кода, и это означает, что в источнике больше прыгает.
+0

Это не кажется разумным. Я собираюсь создать кучу функций, которые будут когда-либо вызваны только с одного места. Затем в «основном» методе, в этом случае export.post, мне придется перевернуть весь файл для этих функций, чтобы следить за потоком кода. Я не уверен, что мне нравится любой из вариантов, тонна одной функции или огромные отступы. – Justin808

+1

Все по-другому. Называть его «безумным», потому что он незнакомый вам близорукий. Это определенно сложно по сравнению с синхронным кодом, но помните, что несправедливо сравнивать набор блокирующих синхронных кодов с асинхронным кодом. Если вы хотите провести сравнение, вы ДОЛЖНЫ использовать многопоточную блокировку, чтобы по крайней мере сравнить сопоставимую функциональность, и в этом случае асинхронный пример обычно будет резко масштабироваться. –

+0

Я полагаю. У моей проблемы есть тонна функций, которые инкапсулируют единицу if/else. Наверное, я предполагаю, что в файле будет 100 крошечных функций, которые просто звонят друг другу, чтобы никогда не использоваться нигде. – Justin808

0

Если вам нужно прокомментировать что-то «загрузить изображение» или «сохранить изображение», извлекая те, на отдельные функции поможет читаемость, уменьшить уровень отступа, а также устранить необходимость в комментариях.

I.e. вместо того, чтобы писать

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

вы можете написать

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

// ... 
     petAHorse(horsePettedHandler); 
// ... 
1

Я обнаружил, что понятие обещания помогает здесь много. В JavaScript существует множество реализаций обещаний, но идея такая же. Вы можете начать с этого:

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

2

<shamelessPlug>

Я прошел через процесс обратного вызова спагетти -> пообещать спагетти -> написал свою собственную библиотеку управления обещание

That library is available here

... and this describes the above process in a bit more detail

</shamelessPlug>

В любом случае, общий ответ - да, в конечном итоге вы столкнетесь с беспорядком обратных вызовов. Чтобы прекратить это, вам лучше всего воспользоваться системой управления обратными вызовами или обещаниями.

Какой метод является лучшим? Это действительно зависит от вас и того, что вы предпочитаете.

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); 
} 

Хорошая часть, что, разделяя все, что в различных функций, которые вы на самом деле разделить логику в блоки. Имя вновь созданной функции говорит о цели блока.

+0

Я вижу это, но моя проблема с этим и почему я этого не делал в первую очередь - это глобальные переменные 'response' и' request'. Кажется, я не могу найти способ передать их методам обратного вызова. По правде говоря, я не могу найти способ передать какую-либо переменную в обратный вызов. – Justin808

+0

Передача одинаковых аргументов для обратных вызовов не очень хороша. Я имею в виду, что у вас может быть множество аргументов. Если вы не хотите быть глобальными (и они глобальны только для текущего модуля), тогда определите замыкание. Выполняя это, вы будете держать их в закрытом пространстве. – Krasimir

0

Вместо обещаний я предпочитаю использовать node-sync lib. Потому что это позволяет одно замечательное: вы не должны обертывать функции async libs для обещаний, вы можете просто использовать его со специальным синтаксисом. Пример:

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

Он работает с вашим собственным кодом таким же образом.