Pull to refresh

Comments 63

Асинхронные запросы синхронизировали, синхронизировали, да не высинхронизовали
TheShock, интересует ваше мнение =)

Прикольно) В принципе, решение достаточно похоже на моё)
На самом деле — зависит от вкусов, что лучше — расширять прототип или использовать класс-враппер, тут можно похоливарить, но не имеет смысла)

Только не нашёл ответа на один вопрос — что будет, если мне надо синхронно выполнить три и больше запросов?

пс. Когда вообще, имхо, придётся использовать эту функцию — так это при асинхронных запросах к базе, если нам необходимо двумя и больше запросами выбрать из разных таблиц пачку данных и сгруппировать в orm.
Если три и более — то же самое. Количество не органичено (ну… в разумных пределах, конечно) =)
… подправил фразу в первой строке.
допустим такую ситуацию.
нам надо указать три функции. на две из будут указаны и выполнены до того, как указана третья функция.
В предыдущем топике, кстати, был такой вопрос

ps. Предыдущая ссылка на топик сломалась, хабрапарсер — лох. вот корректная ссылка
habrahabr.ru/blogs/javascript/108298/
М… не так, как в том вопросе, но такое может произойти.

WaitSync.wrap() возвращает готовую функцию и попутно регистрирует ее у себя (он, кстати, помнит какая именно функция была создана и была ли выполнена). Косяк может возникнуть, если попробовать запустить одну из функций, не сгенерив хотя бы еще одну другую.

Например:
var fail = new WaitSync(function () {
      console.log('epic fail');
});

badFunction = fail.wrap(function () {
     console.log(' imma carryin' damage!');
});

badFunction();

goodFunction = fail.wrap(function () {
    console.log('too late :(');
});

setTimeout(goodFunction, 4000);



но если сделать вызов badFunction() после создания goodFunction, все будет ок.
Что касается побочных вызовов…

var test = new WaitSync(function () {
      console.log('done');
});

var sameFunction = function () {
     console.log('test');
};

var testFn = test.wrap(sameFunction);

var testFn2 = test.wrap(sameFunction);

testFn();   // не вызовет колбэк
testFn2(); // вызовет колбэк


хотя wrap'у передается одна и та же функция, он генерит разные замыкания и регистрирует у себя две разные функции в итоге (поэтому он ждет, пока выполнятся обе, ну или больше).

Однако такой вариант:
var test = new WaitSync(function () {
      console.log('done');
});

var sameFunction = function () {
     console.log('test');
};

var testFn = test.wrap(sameFunction);   // генерит врап для первой функции

var testFn2 = test.wrap(function() {console.log ('somethingelse')}); // врап второй функции 

testFn2();   // не вызовет колбэк, ждем testFn
testFn();     // вызывает колбэк


testFn();     // не вызывает колбэк, т.к. кого надо уже позвали, НО функция будет выполнена 
testFn();     // аналогично


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

var test = new WaitSync(function () {
console.log('done');
});
setTimeout(function () {
test.wrap(fn1);
}, 100);
$.ajax('a', test.wrap(fnA));
отправилось раньше времени
вы не совсем поняли. ну грубо говоря так:
var test = new WaitSync(function () {
  console.log('done');
});
setTimeout(function () {
  $.ajax('c', test.wrap(fnC));
}, 100);
$.ajax('a', test.wrap(fnA));
$.ajax('b', test.wrap(fnB));


именно эта ситуация практически нереальна, но при создании, например игрушки, теоретически может случится. и что тогда если запросы a и b выполнятся раньше, чем добавится fnC?
да, в этом случае до fnC дело может не дойти, т.к. сгенерирована она будет не сразу. Можно перестроить код слегонца…
var test = new WaitSync(function () {
    console.log('done');
});

setTimeout(test.wrap(function () {
    $.ajax('c', 
        test.wrap(fnC)));
}), 100);

$.ajax('a', test.wrap(fnA));
$.ajax('b', test.wrap(fnB));


М… ну как-то так… хотя уже попахивает чем-то путанным. Ну на подобный счет у меня другая мысль, но о ней не тут ) Могу прототип показать, если что, но там оч. сыро)
подсветите код через:
<source lang="javascript">
    // code
</source>
уху, спс. Не часто топики пишу просто)
UFO just landed and posted this here
извиняюсь, если запутал -_-.

Если вам нужен синхронный аджакс запрос, для этого есть параметр async. По умолчанию он true. Можно поставить false.

     $.ajax({
        url: 'something.php',
        async: false
   });


Если вам нужно что-то не только для аджакса… ждите следующего топика ;)
UFO just landed and posted this here
Может, подскажете, как все-таки «превратить» асинхронную функцию в синхронную?

С сохранением всех преимуществ асинхронной на джаваскрипте — нельзя.
Но зачем? Достаточно научится пользоваться асинхронными функциями, и не будет таких непонятных идей
UFO just landed and posted this here
Тогда что вам мешает вызвать эту синхронную функцию в callback асинхронной? Или она не имеет такой возможности?
UFO just landed and posted this here
То, что «выше», можно разделить на две функции и вторую вызвать как callback, вот о чём :-) Это не требует сильного изменения кода, достаточно copy&paste.
UFO just landed and posted this here
Вам нужно привыкнуть к идеологии Javascript :-)
Если вы контролируете код, который «выше», то можно с помощью простой, описанной мной операции, сделать его работоспособным в случае, если используемая в его середине функция асинхронна. А так как в Javascript нет «самой высокой функции» a-la main(), которая выполняется последовательно, то всегда можно переписать последовательный код в код с коллбеками. Т.е. в вашем примере придётся перейти на уровень, куда вы делаете
return result;
и сделать подобную операцию и с ним, и так далее.
Могу привести пример с кодом «до» и «после», если запутанно объясняю :-)
я согласен с Sannis. Вы смотрите на Javascript как на Асм. Но Javascript — это не Асм. Тут совершенно иная идеология и на нём нельзя писать как на асме. На самом деле единственное решение — это разобраться с асинхронными вызовами и использовать таки их.
UFO just landed and posted this here
Тогда эта задача не решается, кроме как while(...). Такова уж концепция.
UFO just landed and posted this here
Нафиг это надо? Почему бы просто синхронно не выставить?
чтобы браузер у пользователя несколько секунд висел? отличная идея)
UFO just landed and posted this here
Ну… не часто нужно такое в браузере. Честно говоря, я сам предпочитаю синхронно подгружать всякие статические списки и опции.

Такое могло бы понадобиться в node.js. Там много построено на асинхронных вызовах и подобная вещь могла бы существенно упростить разработку. К тому же синхронные вызовы там как бы противоречат самой идее ноды )
UFO just landed and posted this here
UFO just landed and posted this here
не знаю от какой библиотеки $ в примере но обычно success и error имеют разные аргументы и в таком виде wrap туда не вставишь.
Как раз-таки, вставишь =)

Он оборачивает функцию и в итоге ей передаются те же параметры, что и в обычной ситуации. Можете попробовать (console.debug(arguments))
Чем вам в данном случае Deferred не угодил? Вы же, по сути, изобрели тоже самое.
Deferred немного для иных целей предназначен. Он позволяет задать callback'и для текущей функии в более удобном формате (хотя с этим тоже можно поспорить, когда callback'ов много, их надо перечислять в определенном порядке и испльзоват в том же порядке… хотя я сильно не юзал его еще). Его тоже можно использовать для данной цели, но вы все равно будете каждый раз создавать отдельную функцию и развешивать ее на callback'и. Привидите пример, как вы будете использовать Deferred для подобной задачи. Думаю, премущество станет очевидным после пары строк лишнего кода =)
как-то
parallel([
	$.get('savannah/get_prey'),
	$.get('savannah/get_other_predators')
]).
next(function (results) {
	console.log(results);
})
Я так понял вы говорите вот об этом jsDeferred, а не о dojo.Deferred

Если да, то каждая функция-аргумент должна возвращать объект Deferred (в dojo тоже). И выглядеть это будет уже по-другому

parallel([
	function () {
             var deferred = new Deferred();
             $.get('savannah/get_prey', deferred.callback());
             return deferred;
        },
	function () {
            var d = new Deferred();
            $.get('savannah/get_other_predators', d.callback());
            return d;
        }
]).
next(function (results) {
	console.log(results);
})

Нехорошо лезть в родительскую библиотеку.
Если все будут работать подобным образом, то что будет если десяток разных «плагинов» будет переопределять одни и те же функции?
я согласен, чтобы этого не делать есть «чистая» библиотека, а там уже делайте хоть $.getAjaxDeferred.
А где такое может понадобиться? Обычно то что делается двумя подряд аякс запросами лучше сделать одним, дело только за проектированием протокола.
Когда вообще, имхо, придётся использовать эту функцию — так это при асинхронных запросах к базе, если нам необходимо двумя и больше запросами выбрать из разных таблиц пачку данных и сгруппировать в orm.

Представим, что нам надо сделать обязательно да, обязательно асинхронных запроса к MySQL на node.js.
Один — для того, чтобы получить статью, второй — чтобы получить комменты к ней.
В одном из сообщений я велосипедил на эту тему:
  get : function (fn) {
    var data = {};
    var set = function (key, value) {
      data[key] = value;
      if ('article' in data && 'comments' in data) fn(data);
    };
    this.getArticle(function (article) {
      set('article', article);
    });
    this.getComments(function (comments) {
      set('comments', comments);
    });
  },


Можно это же самое написать моим решением так:
  get : function (fn) {
    var process = fn.after('article', 'comments')
    this.getArticle (process.article);
    this.getComments(process.comments);
  },

Или воспользоваться решением автора топика.
В правильно спроектированной системе будут три метода — .getArticle, .getComments и .getArticleWithComments.
ну а если .getFileContents, .getArticles, .makeSoapRequest?
то, что я показал и есть метод ".getArticleWithComments", который базируется на методах .getArticle и .getComments. а чо?
Тогда действительно не понятно почему нельзя запросы синхронными. К сожалению не знаком с node.js и его особенностями работы с MySQL. Неужели дополнительный тред на мало-мальски нагруженном сервере даёт выигрыш в производительности?
TheShock зря упростил ситуацию. В реальном приложении скорее всего ситуация будет сложнее: модель может по известному только ей алгоритму определять, откуда брать данные. Простейший пример: если кроме постоянного хранилища (MySQL) используется промежуточный кеш. Тогда вполне возможна ситуация, когда одна из этих функций будет обращаться, а другая нет. Естественно, что в такой ситуации серверу лучше начать рендерить дерево комментариев, если они получены раньше, а после готовности HTML для статьи и комментариев склеивать их и отдавать клиенту. Нда, хотел как лучше объяснить, а сам приводу не самый правильный пример… Но всё же. Кроме того, текущая ситуация с Node.js говорит о том, что выгодно использовать несколько соединений с БД, так что такая ситуация вполне возможна. Node.js сам по себе однопоточный, так что на четырёхядерном сервере можно и несколько потоков для операция с БД использовать.
Почитал немного про node.js… Видимо я до сих пор плыву в плоскости php, где на каждый запрос к серверу работает свой воркер, когда в node.js всё вращается в одном цикле. Тогда действительно без штатных средств мультитрединга, где нагрузку распараллеливать приходится в момент запросов к бд, есть с этим определённые проблемы. Только когда на такие грабли начинаешь натыкаться уже встаёт вопрос об оправданности используемых средств в текущей задаче. Но это я так, мысли в слух…
Оправданность использования MySQL под вопросам, да. В небольших количествах можно. Но от того, что в вышеприведённых примерах заменить MySQL на нативно поддерживающий асинхронность сервер хранения, проблема не исчезает.
Хотел написать то же самое, вы меня опередили.
Понятно, если запросы идут к двум разным ресурсам. Но если к одному — то открывать два или более соединений — неоптимально.
Да, последнее время стало модным велосипедить на эту тему. При этом все реализации в итоге не сильно друг от друга отличаются, по крайне мере на взгляд новичка.
Вообще-то не совсем. Вернее совсем не =) Перечисленные вами библиотеки разворачивают вложенные функции в цепь. и в частности позволяют синхронизировать некоторые вызовы. При этом как и у Deferred не совсем очевидно, какому месту соответствует какая функция.

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

Более или менее похожа вот эта:
https://github.com/isaacs/slide-flow-control/blob/master/lib/async-map.js
и flow-js эм… метод this.MULTI()
но при этом в функцию передаются уже другие аргументы.
Async умеет делать «параллельно», см. https://github.com/caolan/async#parallel. И ещё много других вариантов, включая очередь и возможность не задумываться о порядке в случае, если функции зависят от результата другой. Имхо, очень приятная библиотека.
Пасиб за ссылки. Я, кстати, думал создать вопрос в Q&A сначала и пособирать похожие библиотеки, но вопрос с описанием оказался размером с топик. Так что вы сэкономили мне время =)))
Так это ведь и прекрасно, в этом вся соль. Из многообразия реализаций эволюция отберёт наиболее подходящие.
вот такие смешные декораторы в js…
=) Вы открывали исходники? Да, они самые. Плюс немного прокси :)
Извиняюсь за некропостинг, но по теме очень мало информации, поэтому предложу стандартное решение.
$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //здесь единый колбэк для всех запросов
});
Sign up to leave a comment.

Articles