Параметры синхронизации JavaScript

Мне было интересно, когда существует решение для выполнения синхронизации в коде JavaScript. Например, у меня есть следующий случай: я пытаюсь кэшировать некоторые значения ответа от вызова AJAX, проблема в том, что можно выполнять одновременно несколько вызовов, поэтому это приводит к состоянию гонки в коде. Так что мне очень любопытно найти решение для этого? У кого-нибудь есть идеи, что делать?


person Artem Barger    schedule 09.05.2009    source источник


Ответы (6)


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

Базовый код в jQuery: (не тестировался и не сокращался... но я делал что-то подобное)

var needAllThese = {};

$(function(){

      $.ajax("POST","/somepage.aspx",function(data) {
          needAllThese.A = "VALUE";
      });

      $.ajax("POST","/somepage2.aspx",function(data) {
          needAllThese.B = "VALUE";
      });

      $.ajax("POST","/somepage3.aspx",function(data) {
          needAllThese.C = "VALUE";
      });

      startWatching();
});

function startWatching() {
   if (!haveEverythingNeeded()) {
       setTimeout(startWatching,100);
       return;
   }
   everythingIsLoaded();
}

function haveEverythingNeeded() {
    return needAllThese.A && needAllThese.B && needAllThese.C;
}

function everythingIsLoaded() {
   alert("Everything is loaded!");
}

РЕДАКТИРОВАТЬ: (re: ваш комментарий)

Вы ищете обратные вызовы так же, как это делает jQuery.

   var cache = {};

   function getSomeValue(key, callback) {
       if (cache[key]) callback( cache[key] );

       $.post( "url",  function(data) {
           setSomeValue(key,data);
           callback( cache[key] );
       }); 
   }

   function setSomeValue(key,val) {
        cache[key] = val;
   }

   $(function(){       
        // not sure you would need this, given the code above
        for ( var i = 0; i < some_length; ++i)  {
            $.post( "url", function(data){ 
                setSomeValue("somekey",data); 
            });
        }

        getSomeValue("somekey",function(val){            
             $("#element").txt( val );              
        };            
    });
person Chad Grant    schedule 09.05.2009
comment
код выглядит так: $(function(){ for ( var i = 0; i ‹ some_length; ++i) $.post( url, function(data) { setUpSomeValue(data); }); var setUpSomeValue = (function () { var cache = {} return function( data) { if (!cache[data]) $.post(.....) //обновить кеш новыми данными else return cache[data]; } })( ); Поэтому всякий раз, когда возвращаются два похожих данных, я все еще делаю второй вызов AJAX, потому что я не могу уведомить об обновлении кеша. Вот почему я думаю о том, чтобы сделать там некоторую синхронизацию. - person Artem Barger; 09.05.2009
comment
Честно говоря, я не уверен, что понимаю, как ваше последнее решение поможет мне в моей проблеме. Вы не против немного объяснить? - person Artem Barger; 09.05.2009
comment
Он делает именно то, что вы просили. Использование обратных вызовов... и если вы написали пример кода в своем комментарии, вы используете обратные вызовы в своем вызове .$post, это та же концепция... у вас не должно возникнуть проблем с ее пониманием, если вы не написали код у тебя проблемы с - person Chad Grant; 09.05.2009
comment
Итак, скажем, some_length = 2, следовательно, будет произведено два вызова $.post(). И пусть говорят, что оба вернут одну и ту же дату. Теперь ответ на второй вызов занимает много времени, поэтому первый вызов setSomeValue создаст еще один $.post(), при этом значение не будет обновлено, поэтому второй вызов будет уверен, что в кеше нет значения, и будет вызовите второй раз ненужный код. Поэтому я до сих пор не понимаю, как ваше решение решает этот случай. - person Artem Barger; 09.05.2009
comment
Я указал ненужный код. Я бы рассмотрел возможность перепроектирования вашего приложения/данных, если у вас так много проблем. Я не встречал условий гонки, о которых вы говорите, и я кодирую сервисы jQuery/JSON каждый день в течение 2 лет. Может быть, вы должны получить все даты за один звонок ... кто знает. Как я уже сказал, кода нет, и я просто догадываюсь о вашей проблеме. - person Chad Grant; 10.05.2009
comment
У меня не так много проблем, на самом деле только одна. Я пытаюсь оптимизировать и выполнять как можно меньше вызовов на сервер, а сами вызовы делать как можно легче. В любом случае, ваше решение имеет ту же ОШИБКУ/проблему, и это действительно странно, что вы этого не видите. Вызовы асинхронны, поэтому они независимы, поэтому я дважды вызываю setSomeValue с одним и тем же значением ключа, первый будет продолжать $.post, поэтому будет ждать результата, а кеш не будет обновляться, поэтому второй вместо ожидания выполнит второй ненужный код. Поправьте меня если я ошибаюсь. - person Artem Barger; 10.05.2009

Javascript по своей сути является однопоточным, по крайней мере, для обычной среды браузера. Никогда не будет двух одновременных запусков скриптов, обращающихся к одному и тому же документу. (новые рабочие потоки Javascript могут быть здесь исключением, но они вообще не предназначены для доступа к документам, а только для обмена сообщениями).

person fforw    schedule 09.05.2009
comment
Javascript может быть однопоточным, но у вас могут быть условия гонки, нет гарантии порядка, в котором выполняются асинхронные запросы, когда они завершаются и т. д. Это особенно верно для надстроек firefox и нескольких окон. - person Bjorn; 09.05.2009
comment
вам, очевидно, нужно написать код, который может справиться с проблемами, которые вы описываете. Это не тот тип синхронизации, который вам нужен в других языках, чтобы потоки не мешали друг другу, считывая неполные результаты из памяти или перезаписывая изменения друг друга между более крупными операциями. В среде браузера есть только один активный поток выполнения javascript. все обработчики событий вызываются сериализованным способом один за другим. - person fforw; 09.05.2009
comment
Просто попробуйте написать, например, один вызов AJAX, затем в обработчике вызовите другой запрос AJAX, а во втором обработчике получите доступ к какой-то глобальной переменной. А теперь сделайте несколько итераций первого вызова. Вы все еще уверены, что есть сериализованный доступ к этой переменной? Не могли бы вы предсказать порядок? И еще одно, если бы я мог написать рабочий код для описанной проблемы, я бы никогда не обратился сюда за помощью. - person Artem Barger; 09.05.2009
comment
Порядок непредсказуем, но все ответы будут обрабатываться последовательно, один за другим. Один ответ выигрывает гонку, и вызывается обработчик ответа. если приходит другой ответ, пока работает первый обработчик ответов, второму обработчику ответов придется ждать, пока первый не завершит работу. - person fforw; 09.05.2009
comment
Как только ответы должны состязаться, происходит нарушение прав доступа к глобальным переменным. В моем случае я ясно вижу, что обработчики были вызваны одновременно, и пока первая обновляет переменную, вторая не может ее видеть и поэтому снова выполняет ненужные тяжелые вычисления. - person Artem Barger; 09.05.2009

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

$.post( "url1", function( data)
{
     // do some computation on data and then
     setSomeValue( data);
});

var setSomeValue = ( function()
{
    var cache = {};
    return function( data)
    {
        if ( cache[data] == "updating")
        {
             setTimeout( function(){ setSomeValue( data);}, 100);
             return;
        }
        if ( !cache[date])
        {
             cache[date] = updating;
             $.post( "url2", function( another_data)
             {
                  //make heavy computation on another_data
                  cache[data] = value;
                  // update the UI with value
             });
        }
        else
        {
             //update the UI using cached value
        }
    }
})();
person Artem Barger    schedule 11.05.2009
comment
Спасибо за это. Я удивлен, как много людей сказали, что это однопоточность, так что это не проблема, которая совершенно неверна в мире с асинхронными обратными вызовами. Вы хорошо описали проблему, и для меня это совершенно эффективный подход. - person Stuart Watt; 08.01.2015

Да, вы можете сделать ваши xmlHttpRequests синхронными, в не-IE установите для параметра asynch значение false при открытом методе в браузерах IE сделайте то же самое с параметром bAsync.

Возможно, вы захотите как-то связать свои запросы. Создайте стек очереди и отправляйте запросы по мере продвижения вниз по очереди.

person Bjorn    schedule 09.05.2009
comment
синхронные запросы плохи, потому что они препятствуют выполнению всех других обработчиков событий javascript. - person fforw; 09.05.2009
comment
Я просто ответил на его вопрос, я не пытался судить о его потребности. Понизить это? хе. - person Bjorn; 09.05.2009
comment
Людям может потребоваться синхронизация по разным причинам, например, вы хотите, чтобы запрос обрабатывался до обработки любого другого пользовательского ввода. Есть и другие причины. И т.п. - person Bjorn; 09.05.2009

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

В качестве примечания, браузеры теперь вводят рабочие потоки, которые позволяют параллелизм в JS, но на данный момент это не так.

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

JS дает вам очень хорошее решение с обратными вызовами. Для каждого асинхронного события, которое вы будете отправлять, прикрепите к нему соответствующую функцию обратного вызова, которая будет правильно обрабатывать событие. Синхронизация, в том смысле, в каком вы это имеете в виду, должна происходить там.

person Yuval Adam    schedule 09.05.2009
comment
Хорошо, в моем случае я пытаюсь создать кеш для некоторых ответов с сервера. Существует вызов AJAX, у которого есть собственный обработчик, но в обработчике есть вызов функции, которая обращается к переменной кеша, и если переменная установлена, используйте ее, чтобы сделать еще один тяжелый вызов AJAX. Поэтому, если я использую асинхронный вызов, я, вероятно, могу пропустить обновление кеша и выполнить ненужный вызов дважды, такое поведение нежелательно, поэтому я подумал о том, как я мог бы синхронизировать доступ для чтения/записи этой переменной. - person Artem Barger; 09.05.2009

Я знаю, что это далеко после, но думал, что просто дам улучшение. Артем Баргер разработал собственное решение, которое использует setTimeout, если кешированное значение «обновляется»… Вместо этого просто пометьте функцию в связанной очереди против кешированного значения, чтобы у вас был объект, который содержит фактические кешированные данные и очередь функций. для вызова с кэшированными данными после возврата данных.

Итак, если он «обновляется», просто добавьте свою функцию в очередь, которая связана с данными элемента, и когда данные возвращаются... установите данные, а затем выполните итерацию по очереди функций (обратных вызовов) и вызовите их с возвращенным данные.

если вы хотите получить техническую информацию, вы можете установить определенный тайм-аут QOS при прохождении очереди, установить секундомер в начале итерации, и если вы достигнете определенного времени, затем вызовите setTimeout, чтобы вернуться в очередь, чтобы продолжить итерация ... Надеюсь, вы это поняли :) - В основном это экономит X количество вызовов setTimout, нужен только один.

Просмотрите код Artem Bargers выше, и вы должны понять суть того, что я предлагаю.

person Colin Saxton    schedule 19.09.2017