Web Worker многопоточность в JavaScript

Рассчитаем число Pi в 5000 знаков воспользовавшись Web Worker, setTimeout, и без использования разбора очереди setTimeout или многопоточности(Multithreading) Web Worker, цикл в основном (и единственном) потоке JavaScript. Сравним время выполнения, ощущения по блокировки графического интерфейса (UI). Воспользуемся формулой Джона Мачайна John Machin
π=16arctan(15)4arctan(1239)
arctanx=xx33+x55x77+x99
Web WorkersetInterval(0) (на каждую итерацию)цикл while
Pi
Код под капотом.
Мои итоги, плюс Web Worker? работа в отдельном потоке, никакого влияния на основной поток, отсутствие задержки setTimeout, на каждый вызов порядка 10мс, и это если в это время очередь не занята еще чем-либо, например, визуальными эффектами jQuery, да и при setTimeout приходится модифицировать код задачи, итерации делить по вызовам setTimeout. Минус, хотя это и не минус, по другому быть и не может, отсутствие доступа к не потокобезопасным ресурсам DOM, window, document, parent. Где же использовать практически, однооконные веб-приложения с некими относительно тяжелыми расчетами, мне решать такую задачу пока не приходилось.
И еще, FF тормоз, см. скрин, все три теста на одной машинке.

Варианты тестов на разных браузерах и машинках. Google Chrome Internet Explorer Mozila Firefox

mneti, результат транслитерации — мнети

Значение слова мнети — полагать, мнить, считать возможным, встречается в древнерусских рукописях, о православно-христианских вероучениях, богослужениях.

Center bootbox vertically

Выравниваем алерты, промпты и диалоги Bootbox.js по вертикали если указан класс v-center
bootbox.alert({message: "Прежде необходимо утвердить реестр по всем предприятиям!", className: 'v-center'});
bootbox.alert("Прежде необходимо утвердить реестр по всем предприятиям!");
$(function() {
	$(window).on('show.bs.modal resize', function(e){
		var setVpos = function() {
			$(this).find('.modal-dialog').css("margin-top", function() { return Math.max(0, ($(window).height() - $(this).height()) / 3) } );
		};
		if ($.isWindow(e.target))
			$('.modal.v-center:visible').each(setVpos);
		else if ($(e.target).hasClass('v-center')) 
			$(e.target).each(setVpos);
	})
});

Из таблицы в XML и обратно, for xml, from openxml

Если нужно сохранить результат выполнения запроса, оборачиваем его, и пишем в некий столбец типа xml
". ($saveXml ? "update {$this->db_our}[m_PWStatus] set xmlData = (" : '') . "
select 
tt8.priceN * cnt price,
sum(tt5.selected) over (partition by null) selectedCnt,
sum(coalesce(tt8.priceN * cnt,0)*tt5.selected) over (partition by null) selectedSum,
sum(tt5.moot1) over (partition by tt5.h2_id) smoot1,
sum(tt5.moot2) over (partition by tt5.h2_id) smoot2,
(select count(id) from {$this->db_our}[m_PWSubStatus] pw where pw.deleted is null and pw.statusId=1 and pw.pmesId = h2_id) approvedInPmes,
 * from tt5
 outer apply (
	select et.name etName, ek.name ekName, ek.id ekId, eqs.serialNumber from {$this->db_our}[m_listMeasEqui] eqs
	 inner join {$this->db_our}m_typeMeasEqui et on (et.deleted is null and et.id = eqs.typeMeasEquiID)
	 inner join {$this->db_our}m_equiKinds ek on (ek.deleted is null and ek.id = et.equiKindsID)
	where 1=1 
		and eqs.deleted is null 	
		and eqs.id = tt5.measEquiId 
 ) tt7
 outer apply (
	select tt6.workNameF, tt6.price priceN, tt6.kindId kindId2, tt6.id isPriced from tt6
	where 1=1
	and tt6.documentId = tt5.documentID
	and tt6.entityTypeId = tt5.intEntityType
	and coalesce(tt6.kindId,0) = coalesce(tt7.ekId,0)
	and (coalesce(tt7.ekId,0) not in (30,40) or (tt7.ekId in (30,40) and coalesce(tt6.vl,0) = coalesce(tt5.vr,0)))
 ) tt8

 order by h1_id, h2_id, h3_id, TIName, entity, documentID, vr desc, createdD" .
($saveXml ? "
 for xml raw, BINARY BASE64, root('rows'), XMLSCHEMA('urn:api.mneti.ru') ) 
 where deleted is null and pfId={$p->id} and statusId=2" : '');
raw — построчно в тэгах row,
BINARY BASE64 — бинарные данные преобразовать, если таковые есть
root(‘rows’) — корневой тэг
XMLSCHEMA(‘urn:api.mneti.ru’) — сохранить в том числе и схему (может и пригодится, особенно для внешних систем, данные о типах и размерностях)

Забираем данные, в моем случае, схему для возвращаемых данных описывал подправив сохраненный в результате шаблон схемы
		$sqlReport = [['select * from (',''], ["
select *, sum(priceSum) over (partition by null) priceTotal from (
select h1_id, h2_id, h3_id, h1_name, h2_name, h3_name, entity, vr2 vr, workNameF, sum(cnt) cnt, sum(price) priceSum from (
",
"
group by h1_id, h2_id, h3_id, h1_name, h2_name, h3_name, entity, vr2, workNameF
 ) tt
 order by h1_id, h2_id, h3_id, h1_name, h2_name, h3_name, vr desc, entity, workNameF"]];

		$sql = "
DECLARE @xmlData XML

select @xmlData = xmlData from {$this->db_our}[m_PWStatus] where deleted is null and pfId={$p->id} and statusId=2

DECLARE @handle INT  
DECLARE @PrepareXmlStatus INT  

EXEC @PrepareXmlStatus= sp_xml_preparedocument @handle OUTPUT, @xmlData, ''

".$sqlReport[$report][0]."
SELECT *, (case when ekId in (10) then null else vr end) vr2 FROM OPENXML(@handle, '/rows/ttt:row')
	WITH (
	price decimal(25,3)
	,selectedCnt int
	,selectedSum decimal(38,3)
	,smoot1 int
	,smoot2 int
	,approvedInPmes int
	,selected int
	,inOther int
	,id int
	,h1_id int
	,h2_id int
	,h3_id int
	,h1_name varchar(128)
	,h2_name varchar(128)
	,h3_name varchar(128)
	,h3_extId int
	,documentID int
	,dName varchar(128)
	,entity varchar(255)
	,createdD datetime
	,createdDH varchar(30)
	,docDateH varchar(30)
	,TIName varchar(4000)
	,vr float
	,cnt int
	,measEquiId int
	,pfId int
	,unitPriceId int
	,intEntityType int
	,hashedName varchar(128)
	,originalName varchar(256)
	,moot int
	,moot1 int
	,moot2 int
	,etName varchar(128)
	,ekName varchar(255)
	,ekId int
	,serialNumber varchar(64)
	,workNameF varchar(258)
	,priceN decimal(14,3)
	,kindId2 int
	,isPriced int	
	)
) tt ".$sqlReport[$report][1]."
    
EXEC sp_xml_removedocument @handle
";
Тут есть нюансы, если решили выгружать данные с описанием схемы, то и не забываем указать ее при загрузке, при выгрузке можно не указывать urn схемы, тогда сервер сам подставит что-то вроде этого «urn:schemas-microsoft-com:sql:SqlRowSet22» причем последнее число на свое усмотрение, не наш вариант. И корень не забываем указать.

General callback for multiple actions, jQuery

Ставим общий колбэк завершению нескольких асинхронных действий, воспользуемся «промисом» .promise().
1
2
3
4
jQuery( "#test" ).on( "click", "button", function() {
  var _this = jQuery(this);
  _this.prop( "disabled", true ).text( "Работаем" );
  _this.closest( "div" ).find( "div" ).each(function( i, o ) {
    jQuery( o ).slideToggle( 1000 * ( i + 1 ) );
  }).promise().done(function() {
      _this.prop( "disabled", false).text( "Жмем" );
  });
});

IEEE 754 and browsers, toFixed or toPrecission

Все числа в JavaScript, как целые так и дробные, имеют тип Number и хранятся в 64-битном формате IEEE-754, также известном как «double precision»
Так должно быть, но есть нюансы, отличия в итогах, возьмем простой алгоритм for (n=0.5; n<10; n+=1) console.log(n+0.05, (n+0.05).toFixed(1));
Google Chrome Версия 51.0.2704.103 Internet Explorer 11 Версия 11.0.9600.18314 Ваш браузер
+=0.05toFixed(1)toFixed(20) +=0.05toFixed(1)toFixed(20) +=0.05toFixed(1)toFixed(20)
0.550.60.55000000000000004441 0.550.60.55000000000000000000
1.551.61.55000000000000004441 1.551.61.55000000000000000000
2.552.52.54999999999999982236 2.552.62.55000000000000000000
3.553.53.54999999999999982236 3.553.63.55000000000000000000
4.554.54.54999999999999982236 4.554.64.55000000000000000000
5.555.55.54999999999999982236 5.555.65.55000000000000000000
6.556.56.54999999999999982236 6.556.66.55000000000000000000
7.557.57.54999999999999982236 7.557.67.55000000000000000000
8.558.68.55000000000000071054 8.558.68.55000000000000000000
9.559.69.55000000000000071054 9.559.69.55000000000000000000
Тут, конечно, под большим вопросом где подкрасить красным, Google Chrome выводит честные данные, а ИЕ то что нам нужно :-) Хотя и в ИЕ и в Хроме (тут и ИЕ соблюдает стандарты)
x = 0.1 + 0.2
0.30000000000000004
Кстати, в PHP результат будет 0.3, но это не заслуга, просто у него точность по умолчанию 14 знаков. Дабы уровнять и скорректировать результаты, можем использовать такую замену toFixed, позаимствовал у Angular
function toFixed(number, fractionSize) {
    return +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
}

LAST_VALUE() returns wrong result

LAST_VALUE() возвращает неверный результат, и вот почему
select *,
 first_value(id) over (partition by gr order by id) fv,
 last_value(id) over (partition by gr order by id) lv0, -- неверный результат, совсем не последнее значение в группе
 last_value(id) over (partition by gr order by id RANGE BETWEEN UNBOUNDED PRECED, ING AND CURRENT ROW) lv1, -- неверный результат, вот оно значение для рамки окна по умолчанию
 last_value(id) over (partition by gr order by id RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) lv2,
 first_value(id) over (partition by gr order by id desc) lv3,
 max(id) over () mx,
 max(id) over (partition by gr) mxg,
 max(id) over (partition by gr order by id) mxg1 -- вот и max сломался, добавили применилась рамка окна по умолчанию
 from (values
 (1,1),(2,1),(3,1),
 (4,2),(5,2),
 (6,3),(7,3),(8,3)
) t(id, gr)
order by id, gr
id	gr	fv	lv0	lv1	lv2	lv3	mx	mxg	mxg1
1	1	1	1	1	3	3	8	3	1
2	1	1	2	2	3	3	8	3	2
3	1	1	3	3	3	3	8	3	3
4	2	4	4	4	5	5	8	5	4
5	2	4	5	5	5	5	8	5	5
6	3	6	6	6	8	8	8	8	6
7	3	6	7	7	8	8	8	8	7
8	3	6	8	8	8	8	8	8	8
Читаем документацию :-)
Если предложение ORDER BY не указано, то для рамки окна используется весь раздел. Это относится только к тем функциям, которым не требуется предложение ORDER BY. Если предложение ROWS или RANGE не указаны, а указано предложение ORDER BY, то в качестве значения по умолчанию для рамки окна используется RANGE UNBOUNDED PRECEDING AND CURRENT ROW. Это относится только к тем функциям, которые могут принимать дополнительную спецификацию ROWS или RANGE. Например, ранжирующая функция не может принимать предложение ROWS или RANGE, поэтому данная рамка окна не может использоваться, даже несмотря на наличие предложения ORDER BY, а предложение ROWS или RANGE отсутствует.
И это касается не только SQL Server.

Create an object property from a variable value in JavaScript

Создаем объект со свойством равным значению переменной
switch(_this.data(‘act’)) {
	case ‘setfilter’:
		$.ajax({ url: _this.prop(‘href’), dataType: «json», type: «POST»,
		 data: (datas = $.extend({}, _this.data()), $(‘#performedwork input’).map(function(i,o){ return ( t={}, t[$(o).prop(‘name’)]=$(o).val(), t ); }).each(function(i,o){ $.extend(datas, o) }), datas ),
		 success: function( d ) {
			if (d.error.length) {
				bootbox.dialog({ title: ‘<div class="text-danger">Ошибка</div>‘, message: ‘<div class="text-danger">«</div>‘, onShown: function() { this.find(‘.bootbox-body > div’).text(d.error); } });
			} else {

Free ssl certificate 90 days Let’s Encrypt with auto update ACME client

Воспользуемся бесплатным сертификатом центра сертификации Let’s Encrypt, есть ограничение, сертификат выдается на 90 дней, но центр сертификации поддерживает ACME протокол, что дает возможность обновлять сертификат автоматически. Воспользуемся клиентом Letencrypt.sh is a pure BASH implementation of the ACME protocol used by Lets Encrypt
root@www:/usr/ports # cd /usr/ports/security/letsencrypt.sh
root@www:/usr/ports/security/letsencrypt.sh # make install
====> Compressing man pages (compress-man)
===>  Installing for letsencrypt.sh-0.2.0
===>  Checking if letsencrypt.sh already installed
===>   Registering installation for letsencrypt.sh-0.2.0
Installing letsencrypt.sh-0.2.0...
To use this script you should copy the examples in
/usr/local/etc/letsencrypt.sh/ and at least add a
domain and a contact mail address.

You should also copy the openssl.cnf.sample file in
/usr/local/openssl so you won't get warnings about
it missing.

In order to run the script regularly to update
the certificates add this line to /etc/periodic.conf

weekly_letsencrypt_enable="YES"

Additionally the following parameters can be added to
/etc/periodic.conf

To run the certification renenewal as a different user
weekly_letsencrypt_user="_letsencrypt"
To run a script after the renewal (as root)
weekly_letsencrypt_deployscript="/usr/local/etc/letsencrypt.sh/deploy.sh"
я добавил только одну строчку weekly_letsencrypt_enable=»YES», пусть рутом работает

в domains.txt добавим имена доменов и поддоменов
root@www:/usr/local/etc/letsencrypt.sh # cat domains.txt
mneti.ru www.mneti.ru
в config.sh подправим, это обязательно, папка используется для обмена в процессе аутентификации домена
# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges)
#WELLKNOWN="${BASEDIR}/.acme-challenges"
WELLKNOWN="/usr/local/www/nginx-mneti/.well-known/acme-challenge"
Иначе получим ошибку
root@www:/usr/local/etc/letsencrypt.sh # /usr/local/bin/letsencrypt.sh -c
# INFO: Using main config file /usr/local/etc/letsencrypt.sh/config.sh
Processing mneti.ru with alternative names: www.mneti.ru
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for mneti.ru...
 + Requesting challenge for www.mneti.ru...
 + Responding to challenge for mneti.ru...
ERROR: Challenge is invalid! (returned: invalid) (result: {
  "type": "http-01",
  "status": "invalid",
  "error": {
    "type": "urn:acme:error:unauthorized", 
root@www:/usr/local/etc/letsencrypt.sh # /usr/local/bin/letsencrypt.sh -c
# INFO: Using main config file /usr/local/etc/letsencrypt.sh/config.sh
Processing mneti.ru with alternative names: www.mneti.ru
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for mneti.ru...
 + Requesting challenge for www.mneti.ru...
 + Responding to challenge for mneti.ru...
 + Challenge is valid!
 + Responding to challenge for www.mneti.ru...
 + Challenge is valid!
 + Requesting certificate...
 + Checking certificate...
 + Done!
 + Creating fullchain.pem...
 + Done!
В конфиге виртуального хоста включим ssl, пропишем пути к сертификату и ключу и редирект на https если пришли на http
    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    server {
<------>listen 80;
        listen 443 ssl;

<------>ssl_certificate      /usr/local/etc/letsencrypt.sh/certs/mneti.ru/cert.pem;
<------>ssl_certificate_key  /usr/local/etc/letsencrypt.sh/certs/mneti.ru/privkey.pem;

        server_name  mneti.ru  *.mneti.ru;
<------>access_log /var/log/nginx/mneti.access_log;
        error_log /var/log/nginx/mneti.error_log info;
....
<------>if ($ssl_protocol = "") {
<------>    return 301 https://$server_name$request_uri;
<------>}

        root   /usr/local/www/nginx-mneti;
        index  index.html index.htm index.php;

        location / {
    <-->    try_files $uri $uri/ /index.php;
<------>}

        location ~ \.php$ {
<------>    fastcgi_pass   127.0.0.1:9000;
<------>    fastcgi_index  index.php;
<------>    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
<------>    fastcgi_param  PHP_ADMIN_VALUE "open_basedir=$document_root:/tmp:/var/tmp";
<------>    include        fastcgi_params;
<------>}

    }
Обнаружилась проблемка, часть мобильных браузеров сертификат не признавали, все потому что им нужны промежуточные сертификаты вплоть до корневого, полная цепочка, клиент Letencrypt.sh и об этом позаботился, просто заменим в конфиге нгинкса
# ssl_certificate      /usr/local/etc/letsencrypt.sh/certs/mneti.ru/cert.pem;
ssl_certificate      /usr/local/etc/letsencrypt.sh/certs/mneti.ru/fullchain.pem;