Где то в 2007 году появилась технология long pooling (возможно и раньше, но название получила именно тогда).
На бакэнде есть скрипт, который не возвращает (находится в режиме ожидания, т.е. sleep) данных пока не понадобится отправить на клиент сообщение.
В это же время как такового ajax не было (xhtmlrequest появился какраз в 2007г), и чтобы получить данные с сервера проще всего было сгенерировать javascipt константы инициализации этих данных, переданные в функцию, являющуюся колбеком на их получение. Соответственно чтобы запустить ожидание сообщения на клиенте нужно подключить скрипт с соответствующим url в теге script.
Чтобы длинное подключение не закрылось, long poling во время ожидания периодически должен отсылать какие-нибудь данные, например пробел раз в минуту.
в итоге бакэнд формирует с паузой и возвращает скрипт типа:
................................
messageReceived({user:'vasya',message:'Hi!'});
p.s. мелкие чаты не брезговали периодическими апдейтами, на таймере, я видел локальный чат, который список сообщений представлялся в виде html внутри iframe, в котором в meta refresh было прописано обновляться раз в 5 секунд