code: func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool) (*Subscription, error) {
  	if nc == nil {
  		return nil, ErrInvalidConnection
  	}
  	nc.mu.Lock()
  	// ok here, but defer is generally expensive
  	defer nc.mu.Unlock()
  
  	// Check for some error conditions.
  	if nc.isClosed() {
  		return nil, ErrConnectionClosed
  	}
  	if nc.isDraining() {
  		return nil, ErrConnectionDraining
  	}
  
  	if cb == nil && ch == nil {
  		return nil, ErrBadSubscription
  	}
  
  	sub := &Subscription{Subject: subj, Queue: queue, mcb: cb, conn: nc}
  	// Set pending limits.
  	sub.pMsgsLimit = DefaultSubPendingMsgsLimit
  	sub.pBytesLimit = DefaultSubPendingBytesLimit
  
  	// If we have an async callback, start up a sub specific
  	// Go routine to deliver the messages.
  	if cb != nil {
  		sub.typ = AsyncSubscription
  		sub.pCond = sync.NewCond(&sub.mu)
  		go nc.waitForMsgs(sub)
  	} else if !isSync {
  		sub.typ = ChanSubscription
  		sub.mch = ch
  	} else { // Sync Subscription
  		sub.typ = SyncSubscription
  		sub.mch = ch
  	}
  
  	nc.subsMu.Lock()
  	nc.ssid++
  	sub.sid = nc.ssid
  	nc.subs[sub.sid] = sub
  	nc.subsMu.Unlock()
  
  	// We will send these for all subs when we reconnect
  	// so that we can suppress here if reconnecting.
  	if !nc.isReconnecting() {
  		fmt.Fprintf(nc.bw, subProto, subj, queue, sub.sid)
  		// Kick flusher if needed.
  		if len(nc.fch) == 0 {
  			nc.kickFlusher()
  		}
  	}
  
  	return sub, nil
  }
docs: // subscribe is the internal subscribe function that indicates interest in a subject.