Асинхронное программирование на JavaScript без грязных обратных вызовов - PullRequest
7 голосов
/ 08 февраля 2010

Я хочу превратить асинхронную функцию в синхронную.

function fetch() {
  var result = 'snap!';
  $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
    result = data;
  });
  return result;
}

document.write(fetch());​

См. В действии

Результатом всегда будет 'snap!', Потому что $.getJSON запускается после fetch().

Моя первая идея была:

function fetch() {
  var result = 'snap!';
  $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
    result = data;
  });
  <strong>while (true) {
    if (result != 'snap!') return result;
  }</strong>
}

Это не работает, а также взорвать браузер.

Я читал о генераторах и итераторах в JS 1.7 , но я не знаю, как применить это к моей проблеме.

Этот вопрос на самом деле не о jQuery. Вместо $. GetJSON может быть любой другой асинхронной функцией.

Ответы [ 7 ]

9 голосов
/ 08 февраля 2010

См. Также этот вопрос: Остановите выполнение JavaScript без блокировки браузера

Делать именно то, что вы хотите, не работает. Вы не можете создать синхронность из асинхронности (только наоборот!) В однопоточной среде, управляемой событиями. Вы должны принять асинхронную природу языка и разработать свой собственный непротиворечивый стиль обработки обратных вызовов, чтобы ваш код был читаемым / поддерживаемым. Например, вы можете решить не выполнять никакой реальной работы внутри замыкания обратного вызова, а просто вызвать другую функцию / метод верхнего уровня для ясности.

2 голосов
/ 05 августа 2011

Библиотека TameJS предназначена для решения этой проблемы.

Вы можете написать что-то вроде (не проверено):

var result = 'snap!';
await {
    $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", defer(result));
}
return result;
2 голосов
/ 23 сентября 2010

Существует расширение языка JavaScript под названием StratifiedJS. Он работает в любом браузере и позволяет вам делать это: обрабатывать асинхронные проблемы синхронно / линейно, не останавливая работу браузера.

Вы можете включить стратифицированный JavaScript, например, включив Oni Apollo в вашу веб-страницу, например:

<script src="http://code.onilabs.com/latest/oni-apollo.js"></script>
<script type="text/sjs"> your StratifiedJS code here </script>

И ваш код будет выглядеть так:

function fetch() {  
  return require("http").jsonp(
    "http://api.flickr.com/services/feeds/photos_public.gne?" +
    "tags=cat&tagmode=any&format=json", {cbfield:"jsoncallback"});
}
document.write(fetch());​

Или, если вы действительно хотите использовать jQuery в StratifiedJS:

require("jquery-binding").install();
function fetch() {  
  var url = "http://api.flickr.com/?format=json&...&jsoncallback=?"
  return $.$getJSON(url);
}
document.write(fetch());​

Документы включены http://onilabs.com/docs

2 голосов
/ 08 февраля 2010

Вместо написания вспомогательных методов, таких как fetch, которые возвращают значение, заставьте их принять другую функцию, "приемник", чтобы передать их результат:

function fetch(receiver) {

    $.getJSON("blah...", function(data) {

        receiver(data);
    });
}

Очевидно, что это избыточно, потому что именно так getJSON уже работает, но в более реалистичном примере функция fetch обработает или отфильтрует результат, прежде чем передать его.

Тогда вместо:

document.write(fetch());​

Вы бы сделали:

fetch(function(result) { document.write(result); });

Генераторы могут использоваться для того, чтобы сделать асинхронный код более линейным по стилю. Каждый раз, когда вам нужен был какой-то асинхронный результат, вы вызываете функцию для его запуска, и генератор возобновляет работу, когда результат становится доступным. Там будет немного кода управления, поддерживающего работу генератора. Но это не очень помогает, потому что генераторы не являются стандартными для всех браузеров.

Если вам интересно, вот сообщение в блоге об использовании генераторов для исправления асинхронного кода .

2 голосов
/ 08 февраля 2010

Вы хотите бросить свой собственный $ .getSyncJSON, который использует $ .ajax ({async: false}). Смотри: http://api.jquery.com/jQuery.ajax/.

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

1 голос
/ 08 февраля 2010

Функция нижнего уровня $. Ajax () из jQuery имеет больше опций, включая async: [true|false] (по умолчанию true).

Тем не менее, в большинстве случаев вы должны следовать совету Бена и «принять асинхронную природу языка».

0 голосов
/ 30 декабря 2018

Я знаю это немного поздно, но вы можете избежать обратных вызовов с обещаниями и ожидать в асинхронной функции

deliverResult = (options) => (
  new Promise( (resolve, reject) => {
    $.ajax(options).done(resolve).fail(reject);
  })
)

getResult = async () => {
  let options = {
    type: 'get', 
    url: 'http://yourUrl.com', 
    data: {param1: 'arg1'}
  }
  console.log('waiting ..... ');
  let result = await deliverResult(options);
  console.log('**..waiting ended..**');
  console.log(result);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='button' onclick='getResult()' value='click me'/>
...