@LiguidCool

Как в JS сделать функцию асинхронной?

Изучаю на досуге Node.JS по сайту nodebeginner.ru
Одним из пунктов является реализация ответа сервера в варианте "как надо" и "как не надо". Не буду приводить код оригинала, выложу свой:
app.js (стартовый)
// modules
var server = require('server');
var router = require('router');
var requestHandlers = require('requestHandlers');

var handle = {};
handle["/"] = requestHandlers.index;
handle["/index.htm"] = requestHandlers.index;
handle["/dir"] = requestHandlers.dir;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);

server.js
var http = require("http");
var url = require("url");

function start(route, handle) {
    function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        // console.log("Request for " + pathname + " received.");

        route(pathname, handle, response);

        // response.writeHead(200, {"Content-Type": "text/plain"});
        // response.write("Hello World");
        // response.end();
    }

    http.createServer(onRequest).listen(1337);
    console.log("Server has started.");
}

exports.start = start;

router.js
function route(pathname, handle, response) {
    // console.log("About to route a request for " + pathname);
    if (typeof handle[pathname] === 'function') {
        return(handle[pathname](response));
    } else {
        response.writeHead(404, {"Content-Type": "text/plain"});
        response.write("File not found.");
        console.log("No request handler found for " + pathname);
    }
}

exports.route = route;


requestHandlers.js
function sleep(callback, milliSeconds) {
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
    callback();
}

function index(response) {
    // console.log("Request handler 'index' was called.");
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Request handler 'index' was called.");
    response.end();
}


function dir(response) {
    // console.log("Request handler 'index' was called.");
    sleep(function(){
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write("Request handler 'dir' was called.");
        response.end();
    }, 10000);

}

function upload(response) {
    // console.log("Request handler 'upload' was called.");
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Request handler 'upload' was called.");
    response.end();
}

exports.index = index;
exports.dir = dir;
exports.upload = upload;

Собственно в "как надо" в гайде пишется, что нужно передавать response в requestHandlers.js дабы им можно было пользоваться асинхронно. Как "замедлитель" дана функция sleep.
В общем в моем случае чуда не произошло - приложение при переходе по пути /dir блокируется и другие запросы также ждут 10 секунд. Почему так? Что я сделал не так?

PS
В оригинале автор почему-то не рассмотрел решение этой задачи, а тупо взял готовую функцию exec и использовал её callback.

upd.
Алексей Тен: посоветовал пользовать setTimeout, что сработало. Но все же вопрос актуален, как поправить sleep()?
  • Вопрос задан
  • 1706 просмотров
Решения вопроса 2
@tex0
Алексей Тен: посоветовал пользовать setTimeout

в NodeJS есть метод
process.NextTick(callback(){
//ваш код
});

В Node работает всего два потока (по умолчанию. Вроде бы можно как-то запустить дополнительные event-loop'ы, но это совсем другая история). Первый - main поток, исполняющий ваш код, второй - поток обработки асинхронных операций (EventLoop). Пока вы свой код не поместили в очередь на выполнение, он будет выполняться в синхронном режиме, т.е. в потоке main.
process.NextTick помещает ваш код в очередь и сразу же возвращает управление main-потоку.
Вроде как-то так.
ЗЫ: Поправьте, если я где-то ошибся.
Ответ написан
Комментировать
@anton_ky
process.NextTick помещает ваш код в очередь и сразу же возвращает управление main-потоку.
Вроде как-то так.
ЗЫ: Поправьте, если я где-то ошибся.

Если ставить в очередь с помощью nextTick, то в данном случае вы получите по сути тот-же синхронный вариант.
Дело в том, что nextTick имеем высший приоритет в очереди, и если мы на основании его сделаем "рекурсивный коллбэк" то получим его четко последовательное выполнение, как обычной рекурсии.
function sleep(ms, callback) {
  var cDateMs = (new Date()).getTime() + ms;
  process.nextTick(function waitForTime() {
    if (cDateMs > (new Date()).getTime()) {
      process.nextTick(waitForTime);
    } else {
      callback();
    }
  });
}

Вместо nextTick() нужно использовать setImmidiate.
function sleep(ms, callback) {
  var cDateMs = (new Date()).getTime() + ms;
  setImmediate(function waitForTime() {
    if (cDateMs > (new Date()).getTime()) {
      setImmediate(waitForTime);
    } else {
      callback();
    }
  });
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
edli007
@edli007
full stack, team lead
Помню себя пару лет назад, когда задал тотже вопрос.
Чтобы дать на него ответ, надо понять как работает событийный цикл.

Все функции синхронны, абсолютно все, но, если функция выполняется слишком долго, она станет блокирующей операцией.

Например ты читаешь файл, если ты читаешь его весь сразу, пока он читается, весь поток событий застыл на месте.

Если ты читаешь файл асинхронно, то все происходит точно также, но файл читают маленькими кусочками, чтение одного кусочка - одна функция и каждая следующая функция запускается по событию, а в промежутках между событиями для чтения файла, нода вставит события для запуска других функций.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы