“This is the 18th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

This article is part of the KOA dependency series, the first few of which can also be viewed on the site:

  • Koa depends on the library parseURL
  • Koa relies on the libraries Type-is and Content-Disposition
  • The Koa dependent libraries accept, Content-Type, and cache-Content-Type
  • Koa relies on the libraries encodeURL and escape- HTML
  • Statuses are the libraries that Koa relies on
  • Koa dependent library cookies

On-finished can listen for end events in response. In the handleRequest of the KOA Application, onFinished can listen for exception scenarios in which the RES ends prematurely. In the body of the response, If a stream is being transmitted, use onFinished to listen for the end of the RES to clean up.

On-finished By default, the onFinished event listener and isFinished method are exported.

Let’s look at the implementation of isFinished:

function isFinished (msg) {
  var socket = msg.socket

  if (typeof msg.finished === 'boolean') {
    // OutgoingMessage
    return Boolean(msg.finished || (socket && ! socket.writable)) }if (typeof msg.complete === 'boolean') {
    // IncomingMessage
    return Boolean(msg.upgrade || ! socket || ! socket.readable || (msg.complete && ! msg.readable)) }// don't know
  return undefined
}
Copy the code

IsFinished receives MSG as a parameter. This MSG can be sent as an IncomingMessage or OutgoingMessage. The FINISHED property is used to determine the message type. Then check whether the state is Finish based on the socket status.

Looking at the onFinished implementation, if the listener is already in isFinished state, an asynchronous callback is immediately executed. The asynchrony is implemented using setImmediate or process.nexttick:

var defer = typeof setImmediate === 'function'
  ? setImmediate
  : function (fn) { process.nextTick(fn.bind.apply(fn, arguments))}Copy the code

We then add listener events, which hang on the MSG object’s __onFinished property and queue events. The actual Finish event binding is in the attachFinishedListener function:

function attachFinishedListener (msg, callback) {
  var eeMsg
  var eeSocket
  var finished = false

  function onFinish (error) {
    eeMsg.cancel()
    eeSocket.cancel()

    finished = true
    callback(error)
  }

  // finished on first message event
  eeMsg = eeSocket = first([[msg, 'end'.'finish']], onFinish)

  function onSocket (socket) {
    // remove listener
    msg.removeListener('socket', onSocket)

    if (finished) return
    if(eeMsg ! == eeSocket)return

    // finished on first socket event
    eeSocket = first([[socket, 'error'.'close']], onFinish)
  }

  if (msg.socket) {
    // socket already assigned
    onSocket(msg.socket)
    return
  }

  // wait for socket to be assigned
  msg.on('socket', onSocket)

  if (msg.socket === undefined) {
    // istanbul ignore next: node.js 0.8 patch
    patchAssignSocket(msg, onSocket)
  }
}
Copy the code

The end and Finish events on the MSG are listened for first, and the callback is executed directly if one is fired, then the callback is listened for on the socket if an error or close is received. For older versions of Node, the socket is assignSocket, so there is a patchAssignSocket logic here.

The on-finished library monitors internal events. You can read the source code to learn about the actual event sources. As mentioned earlier, KOA does stream destroy when it receives an on-Finished event.

The purpose of destroy is to destroy a stream, which is used in the setter for response Body in KOA:

if (val instanceof Stream) {
    onFinish(this.res, destroy.bind(null, val))
    // ...
}
Copy the code

If the data is Stream, you can clean it up by passing it directly to Destroy when you receive onFinish. By default, destroy exports a function that takes the stream argument:

function destroy (stream) {
  if (stream instanceof ReadStream) {
    return destroyReadStream(stream)
  }

  if(! (streaminstanceof Stream)) {
    return stream
  }

  if (typeof stream.destroy === 'function') {
    stream.destroy()
  }

  return stream
}
Copy the code

The stream destroy method is still called internally, with some special handling for ReadStream: If you are of type ReadStream, you may receive an open event after calling Destroy, so we added an onOpenClose method to ensure that the stream can be completely destroyed:

function destroyReadStream (stream) {
  stream.destroy()

  if (typeof stream.close === 'function') {
    // node.js core bug work-around
    stream.on('open', onOpenClose)
  }

  return stream
}

function onOpenClose () {
  if (typeof this.fd === 'number') {
    // actually close down the fd
    this.close()
  }
}
Copy the code

The above is all the source code of the two libraries, this part is actually relatively simple, the two libraries mainly on some boundary case for unified processing and packaging, the logic is relatively simple.