1. 程式人生 > >Node.js —— next( )

Node.js —— next( )

關於next主要從三點來進行說明:

  1. next的作用是什麼?
  2. 我們應該在何時使用next?
  3. next的內部實現機制是什麼?

Next的作用

我們在定義express中介軟體函式的時候都會將第三個引數定義為next,這個next就是我們今天的主角,next函式主要負責將控制權交給下一個中介軟體,如果當前中介軟體沒有終結請求,並且next沒有被呼叫,那麼請求將被掛起,後邊定義的中介軟體將得不到被執行的機會。

何時使用Next

從上邊的描述我們已經知道,next函式主要是用來確保所有註冊的中介軟體被一個接一個的執行,那麼我們就應該在所有的中介軟體中呼叫next函式,但有一個特例,如果我們定義的中介軟體終結了本次請求,那就不應該再呼叫next函式,否則就可能會出問題,我們來看段程式碼

app.get('/a',function(req, res,next){
    res.send('sucess');next();});// catch 404 and forward to error handler
app.use(function(req, res,next){
  console.log(404);var err =newError('Not Found');
  err.status =404;next(err);});

app.use(function(err, req, res,next){
  res.status(err.status ||500);
  res.render
('error',{ message: err.message, error:{}});});

傳送請求”/a”,控制檯列印日誌如下:

404
GET /a 5006.837 ms --Error:Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:345:11)

為什麼程式碼會拋異常呢,就是因為我們在res.send之後呼叫了next函式,雖然我們本次的請求已經被終止,但後邊的404中介軟體依舊會被執行,而後邊的中介軟體試圖去向res的headers中新增屬性值,所以就會丟擲上邊的異常。

讀到這你可能會有個疑問,如果我不在res.send後邊呼叫next函式,那後邊定義的404中介軟體是不是永遠都不會被執行到。現在我們刪除res.send後邊next函式呼叫,傳送請求”/xxx”,我們就會發現404中介軟體被執行了,(ㄒoㄒ),這不是和我們之前說的矛盾了嗎,我們的自定義中介軟體沒有呼叫next,但後邊定義的中介軟體仍舊被執行了,這究竟是為什麼呢。看來只能求助原始碼了~~~

Next的內部機制

functionnext(err){...//此處原始碼省略// find next matching layervar layer;var match;var route;while(match !==true&& idx < stack.length){
      layer = stack[idx++];
      match = matchLayer(layer, path);
      route = layer.route;if(typeof match !=='boolean'){// hold on to layerError
        layerError = layerError || match;}if(match !==true){continue;}...//此處原始碼省略}...//此處原始碼省略// this should be done for the layerif(err){
        layer.handle_error(err, req, res,next);}else{
        layer.handle_request(req, res,next);}}

上邊就是express中next的原始碼,為了更容易說明問題,對程式碼進行了刪減。從上邊的原始碼可以發現,next函式內部有個while迴圈,每次迴圈都會從stack中拿出一個layer,這個layer中包含了路由和中介軟體資訊,然後就會用layer和請求的path就行匹配,如果匹配成功就會執行layer.handle_request,呼叫中介軟體函式。但如果匹配失敗,就會迴圈下一個layer(即中介軟體)。

現在我們就能解釋上邊提出的問題了,為什麼我們的自定義中介軟體中沒呼叫next函式,但後邊的404中介軟體仍舊會被執行到,因為我們請求的”/xxx”匹配不到我們註冊的”/a”路由中介軟體,所以while迴圈會繼續往下執行,匹配404中介軟體成功,所以會執行404中介軟體。

注意:app.use註冊的中介軟體,如果path引數為空,則預設為”/”,而path為”/”的中介軟體預設匹配所有的請求。

有一點需要特別指出,其實我們在定義路由中介軟體的時候函式的第三個引數next和我們定義非路由中介軟體的函式的第三個引數next不是同一個next,我們在上邊看到的是非路由中介軟體的next,而路由中介軟體的next函式是這樣的

functionnext(err){if(err && err ==='route'){returndone();}var layer = stack[idx++];if(!layer){returndone(err);}if(layer.method && layer.method !== method){returnnext(err);}if(err){
      layer.handle_error(err, req, res,next);}else{
      layer.handle_request(req, res,next);}}

這個next比上邊的那個next要簡單很多,它負責同一個路由的多箇中間件的控制權的傳遞,並且它會接收一個引數”route”,如果呼叫next(“route”),則會跳過當前路由的其它中介軟體,直接將控制權交給下一個路由。

最後有必要再說一說next(err),next(err)是如何將控制權傳遞到錯誤處理中介軟體的,從前邊的程式碼我們知道,當呼叫next(err)是,express內部會呼叫layer.handle_error,那我們來看看它的原始碼

Layer.prototype.handle_error =function handle_error(error, req, res,next){var fn =this.handle;if(fn.length !==4){// not a standard error handlerreturnnext(error);}try{
    fn(error, req, res,next);}catch(err){next(err);}};

程式碼中的fn就是中介軟體函式,express會對fn的引數個數進行判斷,如果引數個數不等於4則認為不是錯誤處理中介軟體,則繼續呼叫next(err),這樣就會進入到下一個中介軟體函式,繼續進行引數個數判斷,如此方式一直到某個中介軟體函式的引數個數是4,就認為找到了錯誤處理中介軟體,然後執行此中介軟體函式。

轉載地址: