Javascript-форум (https://javascript.ru/forum/)
-   Angular.js (https://javascript.ru/forum/angular/)
-   -   Как в worker-е выполнить SQL-запрос? (https://javascript.ru/forum/angular/67277-kak-v-worker-e-vypolnit-sql-zapros.html)

okuznetsov1 07.02.2017 17:09

Как в worker-е выполнить SQL-запрос?
 
Реализовываю работу с воркером через сервисы ('ng-vkThread','ngWebworker'), в данном примере - 'ngWebworker', но ошибку получаю одну и тужу: ReferenceError: $http is not defined

Помогите, в с чем связана проблема?



app.js

var app = angular.module('contactList', ['lumx','ngSanitize', 'ngCsv', 'ng-vkThread','ngWebworker']);

app.controller('ReadFileController', ['$scope','$http','vkThread','Webworker', function ($scope, $http, vkThread, Webworker) {

                var foo = function (content) {

                                $http.post('/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'}).then(function(response) {


                        $scope.contactslist.push({
                                id: response.data[0].id,
                                last_name: response.data[0].last_name,
                                name: response.data[0].name,
                                patronymic_name: response.data[0].patronymic_name,
                                address: response.data[0].address,
                                telephone: response.data[0].telephone,
                                url: response.data[0].url,
                                description: response.data[0].description,
                                completed: response.data[0].completed
                        });


                });

                                return 1;
                }


        $scope.readContent = function($fileContent){

            $scope.content = $fileContent;

            var myWorker2 = Webworker.create(foo);
            myWorker2.run($scope.content).then(function(result) {
                console.log(result);
            });

        };

}]);

destus 07.02.2017 18:31

okuznetsov1,
Твой вебворкер запускается в другом потоке. То есть контекст воркера и ангуляр приложения полностью изолированы поэтому у тебя нет доступа к сервису $http.

Вот готовое решение https://github.com/FredrikSandell/angular-workers

okuznetsov1 08.02.2017 17:52

Цитата:

Сообщение от destus (Сообщение 443579)
okuznetsov1,
Твой вебворкер запускается в другом потоке. То есть контекст воркера и ангуляр приложения полностью изолированы поэтому у тебя нет доступа к сервису $http.

Вот готовое решение https://github.com/FredrikSandell/angular-workers


Реализовал по вашему решению, вроде бы получилось. Можете подсказать правильно ли сделал? Предполагаю, что есть ошибки, хотя код работает хорошо и без сбоев. Также как проверить что код работает в фоне? (я зацикливал на 1000 записей, а в это время щёлкал по странице и элементам, страница не подвешивалась и можно сказать не подтормаживала. может быть есть какие-то средства специальные для этого?)


var app = angular
            .module('contactList', ['lumx', 'ngSanitize', 'ngCsv', 'FredrikSandell.worker-pool'])
            .run(function (WorkerService) {
            //WorkerService.setAngularUrl('../bower_components/angular/angular.js');
            WorkerService.setAngularUrl('https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js');
            //WorkerService.addDependency(serviceName, moduleName, url);
        });

app.controller('ReadFileController', ['$scope', '$http', 'WorkerService', function ($scope, $http, WorkerService) {

        $scope.foo = function (content) {

            var workerPromise = WorkerService.createAngularWorker(['input', 'output', '$http', function (input, output, $http) {

                //var i=0;
                //var callback = function(i) {
                //  output.notify();
                //  i++;
                //};
                //for (var i = 0; i < 1000; i++) { callback(i); }
    
                output.notify();
            
            }]);

            workerPromise
                .then(function success(angularWorker) {
                    //The input must be serializable
                    return angularWorker.run();
                }, 
                function error(reason) {
                    console.log('callback error');
                    console.log(reason);
                    //for some reason the worker failed to initialize
                    //not all browsers support the HTML5 tech that is required, see below.
                }).then(function success(result) {
                    console.log('success');
                    console.log(result);
                    //handle result
                    }, function error(reason) {
                        //handle error
                        console.log('error');
                        console.log(reason);
                    }, function notify() {
                        //handle update


                        $http.post('/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'})
						  .then(function(response) {

							$scope.contactslist.push({
                                id: response.data[0].id,
                                last_name: response.data[0].last_name,
                                name: response.data[0].name,
                                patronymic_name: response.data[0].patronymic_name,
                                address: response.data[0].address,
                                telephone: response.data[0].telephone,
                                url: response.data[0].url,
                                description: response.data[0].description,
                                completed: response.data[0].completed
							});
							
						  }

                    }
                );
        }



	}]);

destus 08.02.2017 18:30

okuznetsov1,
:)
Код твоего воркера находится в строчках с 14 по 24. Как видишь, твой воркер синхронно возвращает уведомление что он завершил работу и твой http запрос выполняется в основном потоке, т.е. ничего полезного воркер тут не делает как видишь.

Я подозреваю, что ты хотел выполнять запросы в теле воркера, следовательно тебе надо перенести строку 48 в тело воркера и оттуда в then коллбеке уведомлять главный поток, что запрос завершен и можно обновлять состояние contactslist.

$http.post('/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'})
                          .then(function(response){
output.notify(response.data[0]);
})

function notify(data) {
...
}


Цитата:

может быть есть какие-то средства специальные для этого?)
Код создания воркера уже покрыт юнит тестами.

okuznetsov1 08.02.2017 19:44

Цитата:

Сообщение от destus (Сообщение 443681)
,
Я подозреваю, что ты хотел выполнять запросы в теле воркера, следовательно тебе надо перенести строку 48 в тело воркера и оттуда в then коллбеке уведомлять главный поток, что запрос завершен и можно обновлять состояние contactslist.

Да, в теле воркера. Я пробовал так как вы посоветовали, но получаю эту ошибку в Firefox: "Error: An invalid or illegal string was specified
rg/<@http://www.xxx.com/anguler-tasks-contacts/js/angular.min.js:106:201.........."


Если запускаю в Ghrome, то ошибка выглядит так: "XMLHttpRequest cannot load . Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource."


var app = angular
            .module('contactList', ['lumx', 'ngSanitize', 'ngCsv', 'FredrikSandell.worker-pool'])
            .run(function (WorkerService) {
            //WorkerService.setAngularUrl('../bower_components/angular/angular.js');
            WorkerService.setAngularUrl('https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js');
            //WorkerService.addDependency(serviceName, moduleName, url);
        });

app.controller('ReadFileController', ['$scope', '$http', 'WorkerService', function ($scope, $http, WorkerService) {

        $scope.foo = function (content) {

            var workerPromise = WorkerService.createAngularWorker(['input', 'output', '$http', function (input, output, $http) {

                //var i=0;
                //var callback = function(i) {
                //  output.notify();
                //  i++;
                //};
                //for (var i = 0; i < 1000; i++) { callback(i); }

                $http.post('/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'});

//                $http.post('/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'})
//                          .then(function(response){
//                            output.notify(response);
//                        });
            
            }]);

            workerPromise
                .then(function success(angularWorker) {
                    //The input must be serializable
                    return angularWorker.run();
                }, 
                function error(reason) {
                    console.log('callback error');
                    console.log(reason);
                    //for some reason the worker failed to initialize
                    //not all browsers support the HTML5 tech that is required, see below.
                }).then(function success(result) {
                    console.log('success');
                    console.log(result);
                    //handle result
                    }, function error(reason) {
                        //handle error
                        console.log('error');
                        console.log(reason);
                    }, function notify(response) {
                            //handle update
                            console.log(response.data[0]);
                    }
                );

        };

}]);

okuznetsov1 08.02.2017 21:42

С этим разобрался, проблема оказалась из за того, что нужно явно было указать "http://", т.е. так:

$http.post('http://www.xxx.com/anguler-tasks-contacts/addContactData.php',{last_name:1, name:2, patronymic_name:3, address:4, telephone:5, url:6, description:7, completed:'false'});

okuznetsov1 08.02.2017 21:56

Цитата:

Сообщение от destus (Сообщение 443681)
okuznetsov1,

Не понимаю как внутри workera вызвать $scope-функцию angular-приложения, т.е. мне нужно вызвать функцию ($scope.parseCSV(content)) ?


............

 $scope.foo = function (content) {

    var workerPromise = WorkerService.createAngularWorker(['input', 'output', '$http', function (input, output, $http) {


        var Items = parseCSV($content);

         angular.forEach(Items, function (value, key) {
                        $http.post('http://www.xxx.com/anguler-tasks-contacts/addContactData.php',{last_name:value[1], name:value[2], patronymic_name:value[3], address:value[4], telephone:value[5], url:value[6], description:value[7], completed:'false'})
                               .then(function(response){
                                    output.notify(response.data[0]);
                                });
          });

        }

    }

............

destus 09.02.2017 06:30

Цитата:

Не понимаю как внутри workera вызвать $scope-функцию angular-приложения, т.е. мне нужно вызвать функцию ($scope.parseCSV(content)) ?
One can not communicate data between worker and main thread by using service states. All communication must be done through the input object and output promise.

т.е. передавай функцию parseCSV в виде input.

okuznetsov1 09.02.2017 14:51

Цитата:

Сообщение от destus (Сообщение 443725)
One can not communicate data between worker and main thread by using service states. All communication must be done through the input object and output promise.

т.е. передавай функцию parseCSV в виде input.

т.е. мне нужно сделать как то так:

var param = new function() {
  this.content = content;
  this.funcParseCSV = "function parseCSV(input.content) { ........... }";
};


и передать в webworker:

return angularWorker.run(param);


а в worker-e уже как то так:

console.log( (new Function('', input.funcParseCSV))() );

destus 09.02.2017 15:03

okuznetsov1,
не понял этих костылей с new Function. Если мне надо передать просто функцию, то я её объявлю в основном потоке и передам параметром. А если нужно передать функцию и ещё что-то, то лучше сформировать объект и его передавать
var obj = {
  parseCSV: $scope.parseCSV,
  foo: 'bar'
}
angularWorker.run(obj)

okuznetsov1 09.02.2017 15:38

Цитата:

Сообщение от destus (Сообщение 443782)
okuznetsov1,
не понял этих костылей с new Function. Если мне надо передать просто функцию, то я её объявлю в основном потоке и передам параметром. А если нужно передать функцию и ещё что-то, то лучше сформировать объект и его передавать
var obj = {
  parseCSV: $scope.parseCSV,
  foo: 'bar'
}
angularWorker.run(obj)

попробовал и получаю ошибку:

DOMException [DataCloneError: "The object could not be cloned."
code: 25
nsresult: 0x80530019
location: http://www.xxx.com/anguler-tasks-con...kers.min.js:1]


а если сделать так, то функция отрабатывает :

var param = { content: $fileContent, funcParseCSV: "function parseCSV(content) { console.log('ok'); } parseCSV();" };
angularWorker.run(obj);

а в воркере:

eval(input.funcParseCSV);


Но мне так не хотелось бы делать, т.к. лучше вариант - определить эту функцию внутри worker-a

destus 09.02.2017 16:35

okuznetsov1,
А, ну да, input -- сериализуемым оъектом должен быть, для ф-ции не подходит. Тогда твой 1 вариант, ок.

А зачем вообще эту ф-цию в scope объявлять и пытаться передавать? Может лучше сделать чистую функцию, объявить её в теле самого воркера и применять? А Content как раз передавать первым параметром run().

okuznetsov1 09.02.2017 17:20

Цитата:

Сообщение от destus (Сообщение 443803)
okuznetsov1,
А, ну да, input -- сериализуемым оъектом должен быть, для ф-ции не подходит. Тогда твой 1 вариант, ок.

А зачем вообще эту ф-цию в scope объявлять и пытаться передавать? Может лучше сделать чистую функцию, объявить её в теле самого воркера и применять? А Content как раз передавать первым параметром run().

Я вчера ещё объявил эту функцию в теле воркера и применял, всё работало и думал на этом варианте остановиться. Но поразмыслив, пришёл к выводу, что на всякий случай лучше передавать эту функцию в воркер, т.к. в ближайшем будущем в функционале системы могут появится ещё воркеры которые будут использовать эту же функцию, и придётся её дублировать.

вроде получилось как хотел, сделал так:

передаю в воркер:

var bodyFuncParseCSV = eval($scope.parseCSV).toString();
var param = { content: $fileContent, funcParseCSV: bodyFuncParseCSV };
return angularWorker.run(param);


в теле воркера вызываю так:

var Items = eval('var parseCSV = ' + input.funcParseCSV + '; parseCSV(input.content);');


Не знаю костыль это или нет, как вы считаете?

destus 09.02.2017 17:27

okuznetsov1,
обычно eval не рекомендуют к использованию.
Цитата:

в ближайшем будущем в функционале системы могут появится ещё воркеры которые будут использовать эту же функцию, и придётся её дублировать.
А что мешает выделить эту функцию в тело отдельного воркера и запускать его когда нужно? Т.е. сначала запускается воркер, который обладает функциональностью parseCSV, а когда он распарсит, в его коллбэке вызывать другой воркер, который будет делать http запрос на основе этих данных?

okuznetsov1 09.02.2017 18:53

Цитата:

Сообщение от destus (Сообщение 443810)
okuznetsov1,
обычно eval не рекомендуют к использованию.

А что мешает выделить эту функцию в тело отдельного воркера и запускать его когда нужно? Т.е. сначала запускается воркер, который обладает функциональностью parseCSV, а когда он распарсит, в его коллбэке вызывать другой воркер, который будет делать http запрос на основе этих данных?

Ваш данный вариант то что нужно. А не могли бы вы на небольшом примере показать как это реализовать (хотя бы схематично)? Я не могу разобраться как запустить второй воркер в коллбэке

destus 10.02.2017 06:41

okuznetsov1,
$scope.content = 'blabla';

var workerPromise1 = WorkerService.createAngularWorker(['input', 'output',   
    function(input, output) {  
		function parseCSV(input){
			/* body parseCSV */
			return parseInput
		}
		output.notify(parseCSV(input));
  }]);
  
var workerPromise2 = WorkerService.createAngularWorker(['input', 'output', '$http',   
    function(input, output, $http) {
		angular.forEach(input, function (value, key) {
                        $http.post('http://www.xxx.com/anguler-tasks-contacts/addContactData.php',{last_name:value[1], name:value[2], patronymic_name:value[3], address:value[4], telephone:value[5], url:value[6], description:value[7], completed:'false'})
                               .then(function(response){
                                    output.notify(response.data[0]);
                                });
          });
  }]);
  
  workerPromise1.then(function success(angularWorker) {  
    //The input must be serializable  
    return angularWorker.run($scope.content);    
  }, function error(reason) {  
    //for some reason the worker failed to initialize  
    //not all browsers support the HTML5 tech that is required, see below.  
  }).then(function success(result) {  
    //handle result  
  }, function error(reason) {  
    //handle error  
  }, function notify(Items) {  
    workerPromise2.then(function(angularWorker){
		return angularWorker.run(Items)
	}).then(
		function(){},
		function(){},
		function notify(response){console.log(response)}
	) 
  });

okuznetsov1 10.02.2017 16:28

Цитата:

Сообщение от destus (Сообщение 443856)
okuznetsov1,
$scope.content = 'blabla';

var workerPromise1 = WorkerService.createAngularWorker(['input', 'output',   
    function(input, output) {  
		function parseCSV(input){
			/* body parseCSV */
			return parseInput
		}
		output.notify(parseCSV(input));
  }]);
  
var workerPromise2 = WorkerService.createAngularWorker(['input', 'output', '$http',   
    function(input, output, $http) {
		angular.forEach(input, function (value, key) {
                        $http.post('http://www.xxx.com/anguler-tasks-contacts/addContactData.php',{last_name:value[1], name:value[2], patronymic_name:value[3], address:value[4], telephone:value[5], url:value[6], description:value[7], completed:'false'})
                               .then(function(response){
                                    output.notify(response.data[0]);
                                });
          });
  }]);
  
  workerPromise1.then(function success(angularWorker) {  
    //The input must be serializable  
    return angularWorker.run($scope.content);    
  }, function error(reason) {  
    //for some reason the worker failed to initialize  
    //not all browsers support the HTML5 tech that is required, see below.  
  }).then(function success(result) {  
    //handle result  
  }, function error(reason) {  
    //handle error  
  }, function notify(Items) {  
    workerPromise2.then(function(angularWorker){
		return angularWorker.run(Items)
	}).then(
		function(){},
		function(){},
		function notify(response){console.log(response)}
	) 
  });


Спасибо, очень мне помогли.

Но сейчас из всего вышесказанного у меня возник ещё один вопрос (наверное последний):

У меня возникла необходимость (как ранее я и предполагал) задействовать 2-ой воркер (workerPromise2) для расширения функционала системы. В свзи с этим возник вопрос - "как можно запустить второй воркер из другого контроллера", возможно ли такое? Сейчас я продублировал код 2-го воркера в другом контроллере. Но такое решение разумеется мне не нравится.
По сути в другом контроллере я разместил такой же код (что вы прислали в последнем сообщении), выглядит так:

app.controller('MainController', ['$scope', 'WorkerService', 'LxNotificationService', '$http', '$log', function($scope, WorkerService, LxNotificationService, $http, $log) {

    $scope.content = 'blabla';

    $scope.addContact = function() { 

         var workerPromise2 = WorkerService.createAngularWorker(['input', 'output', '$http',   
         function(input, output, $http) {
		angular.forEach(input, function (value, key) {
                        $http.post('http://www.xxx.com/anguler-tasks-contacts/addContactData.php',{last_name:value[1], name:value[2], patronymic_name:value[3], address:value[4], telephone:value[5], url:value[6], description:value[7], completed:'false'})
                               .then(function(response){
                                    output.notify(response.data[0]);
                                });
                });
        }]);
  

        workerPromise2.then(function(angularWorker){
		return angularWorker.run(Items)
	}).then(
		function(){},
		function(){},
		function notify(response){console.log(response)}
	) 

    };

}]);

destus 10.02.2017 17:06

okuznetsov1,
А может лучше тогда в сервисе этот код воркера обьявить, инжектить сервис в каждый контроллер и оттуда вызывать?

okuznetsov1 20.02.2017 18:44

спасибо


Часовой пояс GMT +3, время: 08:05.