node.js — взаимная переменная

Я новичок в node.js, поэтому перед выпуском моего приложения node.js мне нужно убедиться, что оно будет работать должным образом.

Допустим, у меня есть переменная массива, и я инициализирую ее в начале своего скрипта.

myArray = [];

затем я извлекаю некоторые данные из внешнего API, сохраняю их внутри myArray и использую метод setInterval() для повторного извлечения этих данных каждые 30 минут:

pullData();
setInterval(pullData, 30*60*1000);

Выполнение функции pullData() занимает около 2-3 секунд.

Клиенты смогут получить myArray с помощью этой функции:

http.createServer(function(request, response){
var path = url.parse(request.url).pathname;
if(path=="/getdata"){

    var string = JSON.stringify(myArray);
    response.writeHead(200, {'Content-Type': 'text/plain'});
    response.end(string);              

}
}).listen(8001);

Итак, я спрашиваю, может ли произойти следующая ситуация?: Клиент пытается получить данные с этого сервера node.js, и в тот же момент данные записываются в myArray функцией pullData(), в результате чего недействительные данные отправлено клиенту?

Я прочитал некоторую документацию и понял, что когда работает pullData(), createServer() не будет отвечать клиентам, пока pullData() не завершит свою работу? Я действительно плохо разбираюсь в параллельном программировании, поэтому мне нужно ваше подтверждение по этому поводу, или если у вас есть какое-то лучшее решение?

РЕДАКТИРОВАТЬ: вот код моей функции pullData():

 var now = new Date();

Date.prototype.addDays = function(days){

        var dat = new Date(this.valueOf());
        dat.setDate(dat.getDate() + days);
        return dat;
}


var endDateTime = now.addDays(noOfDays);
var formattedEnd = endDateTime.toISOString(); 

var url = "https://api.mindbodyonline.com/0_5/ClassService.asmx?wsdl";
    soap.createClient(url, function (err, client) {
        if (err) {
            throw err;
        }

        client.setEndpoint('https://api.mindbodyonline.com/0_5/ClassService.asmx');
        var params = {
            "Request": {
                "SourceCredentials": {
                    "SourceName": sourceName,
                    "Password": password,
                    "SiteIDs": {
                        "int": [siteIDs]
                    }
                },
                "EndDateTime" : formattedEnd

            }
        };


client.Class_x0020_Service.Class_x0020_ServiceSoap.GetClasses(params, function (errs, result) {
            if (errs) {
                console.log(errs);
            } else {

                    var classes = result.GetClassesResult.Classes.Class;
                    myArray = [];

                    for (var i = 0; i < classes.length; i++) {
                        var name = classes[i].ClassDescription.Name;
                        var staff = classes[i].Staff.Name;
                        var locationName = classes[i].Location.Name;
                        var start = classes[i].StartDateTime.toISOString();
                        var end = classes[i].EndDateTime.toISOString();
                        var klasa = new Klasa(name,staff,locationName,start,end);

                        myArray.push(klasa);
                    }

                    myArray.sort(function(a,b){
                        var c = new Date(a.start);
                        var d = new Date(b.start);
                        return c-d;
                    });

                    string = JSON.stringify(myArray);
     }
        })


    });

person kecman    schedule 20.12.2015    source источник
comment
Почему бы вам не использовать базу данных для хранения данных? Кэшируйте его, если вам нужна производительность. Кроме того, если вы начнете масштабирование с нескольких машин, на каждой машине будет свой myArray, и ответ, который получит клиент, будет зависеть от машины, которая получает запрос! Это будет отлично работать для небольшого сервера, и данные будут храниться до тех пор, пока вы не перезапустите сервер!   -  person rohithpr    schedule 20.12.2015
comment
Это не такая проблема, данных для хранения не так много, и при каждом запуске сервера данные извлекаются снова, поэтому нет проблем с потерей данных при перезапуске сервера. Я бы не стал усложнять это базой данных. Спасибо за ответ в любом случае :)   -  person kecman    schedule 20.12.2015


Ответы (4)


Нет, NodeJs не является многопоточным, и все выполняется в одном потоке, это означает, что, кроме неблокирующих вызовов (т. е. ввода-вывода), все остальное будет задействовать ЦП до тех пор, пока не вернется, а NodeJS абсолютно не возвращает заполненный наполовину массив в конечный пользователь, если вы выполняете только один вызов HTTP для заполнения массива.

Обновление: Как указал @RyanWilcox, любой асинхронный (неблокирующий системный вызов) вызов может подсказать интерпретатору NodeJS, что нужно оставить выполнение вашей функции на полпути и вернуться к нему позже.

person Boynux    schedule 20.12.2015
comment
пока вы делаете только один HTTP-вызов для заполнения вашего массива. ‹-- очень важный момент... хотя это не просто вызовы HTTP, а любая асинхронная операция. - person RyanWilcox; 20.12.2015
comment
@Boynux, пожалуйста, посмотрите на мою функцию pullData(), я только что добавил ее в первый пост, и посмотрите, будет ли она работать нормально? - person kecman; 20.12.2015
comment
Трудно ответить, если я предполагаю, что метод GetClasses из мыльного клиента не выполняет никаких асинхронных вызовов и просто вызывает ваш обратный вызов с данными, да, это безопасно. - person Boynux; 20.12.2015
comment
GetClasses не является методом клиента мыла, это конечная точка API SOAP. Значит, это должно быть безопасно? - person kecman; 20.12.2015
comment
Правильно, это прокси для вашего класса SOAP. Мое лучшее предположение, что да, это безопасно. Это из моего понимания SOAP. Но все же я не уверен в реализации клиента JS SOAP. Поэтому это всего лишь предположение :) - person Boynux; 20.12.2015

В целом: Нет.

JavaScript является однопоточным. Пока работает одна функция, никакая другая функция не может быть запущена.

Исключение составляют задержки между функциями, которые обращаются к значению массива.

e.g.

var index = i;
function getNext() {
    async.get(myArray[i], function () {
        i++;
        if (i < myArray.length) {
            getNext()
        }
    });
}

… в этом случае массив может обновляться между вызовами асинхронной функции.

Вы можете смягчить это, создав deep копию массива при запуске первой асинхронной операции.

person Quentin    schedule 20.12.2015
comment
Спасибо за ваш обширный ответ. Моя функция pullData обновляет myArray только раз в 30 минут, так что все должно быть в порядке;) - person kecman; 20.12.2015

Javascript — это однопоточный язык, поэтому вам не нужно беспокоиться о таком параллелизме. Это означает, что никакие две части кода не выполняются одновременно. В отличие от многих других языков программирования, javascript имеет другую модель параллелизма, основанную на цикле событий. Для достижения наилучшей производительности следует использовать неблокирующие операции, обрабатываемые функциями обратного вызова, промисами или событиями. Я предполагаю, что ваш внешний API предоставляет некоторые функции асинхронного ввода-вывода, которые хорошо подходят для node.js.

person madox2    schedule 20.12.2015

Если ваш вызов pullData не занимает слишком много времени, другим решением является кэширование данных.

Извлекайте данные только тогда, когда это необходимо (например, когда клиент обращается к /getdata). Если он получен, вы можете кэшировать данные с отметкой времени. Если /getdata вызывается снова, проверьте, не старше ли кэшированных данных более 30 минут, и, если да, извлеките их снова.

Также разбор массива в json..

var string = JSON.stringify(myArray);

..может быть сделано вне вызова /getdata, поэтому это не нужно делать для каждого клиента, посещающего /getdata. Может сделать чуть быстрее.

person peerbolte    schedule 20.12.2015
comment
Извлечение данных только тогда, когда это необходимо, не вариант, из-за ограничения количества вызовов API, это должно быть раз в 30 минут, а свежесть извлеченных данных не так важна, 30 минут в порядке. И спасибо за совет по перемещению строки кода stringify() из вызова /getdata, это должно немного ускорить ответы. - person kecman; 20.12.2015
comment
Возможно, мой ответ был недостаточно ясен. Вы не будете получать чаще, чем каждые 30 минут, если правильно используете кеширование. Он будет извлекаться снова, только если кэшированные данные старше 30 минут. На самом деле, если за два часа будет только один посетитель, у вас будет только один вызов апи вместо четырех (четыре обращения нужны при использовании интервала в 30 минут). - person peerbolte; 21.12.2015