簡體   English   中英

避免節點中發生回調地獄

[英]Avoiding callback hell in node

我有這個功能

/* GET main page */
router.get('/', (req, res, next) => {
    Account.find({accType: 'Employer'}, (err, col) => {
        if (err) {
            console.log(err);
        } else {
            Banner.findOne((err, banner) => {
                if (err) {
                    console.log(err);
                } else {
                    // Determine whether there is at least one awaiting account.
                    Account.findOne({accType: 'Awaiting'}, (err, doc) => {
                            if (err) {
                                console.log(err);
                            } else {
                                if (doc != null) {
                                    var awaiting = true;
                                }
                                console.log(doc);
                                res.render('admin/', {
                                    title: 'Admin pannel',
                                    user: req.user,
                                    employers: col,
                                    areAwaiting: awaiting,
                                    banner: banner,
                                )
                            }
                        }
                    );
                }
            });
        }
    });
});

我嘗試使用如下異步模塊:

async.parallel(calls, (err, col) => {
    if (err)
        console.log(err);
    else {
        console.log(col);
        res.render('admin/', {
            title: 'Admin pannel',
            user: req.user,
            employers: col[0],
            banner: col[1],
            areAwaiting: col[2],
        })
    }
});

現在。 我得到的錯誤是wrapAsync(...)不是一個函數 我試圖將我的函數放在這樣的匿名函數中:

() => {
    Account.find({accType: 'Employer'}, (err, col) => {
        if (err)
            console.log(err);
        else return col;
    })
}

但是隨后代碼凍結了。 我也試過這個:

(col) => {
    Banner.findOne((err, doc) => {
        if (err)
            console.log(err);
        else return doc;
    });
    return col
},

但是效果一樣 知道我在做什么錯嗎? 回調地獄版本可以使用,但是很丑陋且不可維護。

戴夫·米漢(Dave Meehan)回答。 必須對其進行略微編輯以使其起作用。

router.get('/', (req, res) => {
    return Promise.all([
        Account.find(),
        Banner.findOne(),
        Account.findOne({accType: 'Awaiting'}),
    ]).then(([employers, banner, awaiting]) => { // Right here
        if (awaiting != null)
            var areAwaiting = true;
        res.render('admin/', {
            title: 'Admin pannel',
            user: req.user,
            employers: employers,
            areAwaiting: areAwaiting,
            banner: banner,
        });
    }).catch(e => {
        console.error(e)
    });
});

我不得不關閉陣列中then進入()

通過不將其寫為回調地獄總是可以避免的。 不過,在這種情況下,讓我們先重寫一些代碼。

首先,如果收到錯誤,您可以提早紓困,因此無需在所有調用中使用else語句。 這將為您消除很多范圍。

因此,只需重寫部分代碼,您就可以將其變成如下所示的形式:

router.get('/', (req, res, next) => {                       
  getData((err, data) => {                                  
    if (err) return console.error(err)                                        
    res.render('admin/', { /* .. */ })                      
  })                                                        
})                                                          

function getData (cb) {                                     
  Account.find({accType: 'Employer'}, (err, col) => {       
    if (err) return cb(err)                                 
    Banner.findOne((err, banner) => {                       
      if (err) return cb(err)                               
      Account.findOne({accType: 'Awaiting'}, (err, doc) => {
        if (err) return cb(err)                             
        cb(null, { col, banner, doc })                      
      })                                                    
    })                                                      
  })                                                        
}                                                           

現在,讓我們回到async的用法上,在這里使用它是一個好主意,因為實際上不知道要依次調用Account.find()Banner.findOne()Account.findOne()

您的問題是您使用的async.parallel錯誤,它假定函數數組(或對象)具有回調。

看起來可能如下所示:

async.parallel({                                       
  col: cb => Account.find({accType: 'Employer'}, cb),  
  banner: cb => Banner.findOne(cb),                    
  doc: cb => Account.findOne({accType: 'Awaiting'}, cb)
}, (err, result) => {                                  
  // access result.col, result.banner and result.doc   
})                                                     

現在我們可以重構上面的getData ,因此最終結果可能類似於:

router.get('/', (req, res, next) => {                              
  getData((err, data) => {                                         
    if (err) return console.error(err)                                               
    res.render('admin/', { /* data.doc, data.banner, data.doc */ })
  })                                                               
})                                                                 

function getData (cb) {                                            
  async.parallel({                                                 
    col: cb => Account.find({accType: 'Employer'}, cb),            
    banner: cb => Banner.findOne(cb),                              
    doc: cb => Account.findOne({accType: 'Awaiting'}, cb)          
  }, cb)                                                           
}                                                                  

這看起來很干凈,實際上不需要使用Promises。

這大概就變得簡單了,並假設:

  1. 您正在使用Express或類似的路由處理程序,並且可以返回承諾而不是使用回調

  2. 您正在使用Mongoose或類似的DB,並且可以返回一個Promise而不是使用回調。

檢查您的版本以獲得Promise支持。

您似乎至少缺少一些Banner的查詢參數,並且需要弄清楚其中的任何一個是否相關,或者如圖所示,它們可以並行運行。

router.get('/', (req, res) => {
  return Promise.all([
    Account.find(),
    Banner.findOne(),
    Account.findOne({ accType: 'Awaiting' }),
  ]).then(([ col, banner, doc ]) => {
    res.render('admin/', {
      title: 'Admin pannel',
      user: req.user,
      employers: col,
      areAwaiting: awaiting,
      banner: banner,
    );
  }).catch(e => { console.error(e) });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM