Как вызвать асинхронную функцию JavaScript и заблокировать исходный вызывающий - PullRequest
17 голосов
/ 09 декабря 2011

У меня интересная ситуация, для которой мой обычно умный ум не смог найти решение :) Вот ситуация ...

У меня есть класс, у которого есть метод get ()... этот метод вызывается для получения сохраненных пользовательских настроек ... он обращается к какому-либо базовому провайдеру для фактического получения данных ... как написано сейчас, он вызывает провайдера, который говорит куки-файлы ... итак, получаем() вызывает providerGet (), скажем, providerGet () возвращает значение и get () передает его вызывающей стороне.Вызывающий ожидает ответ прежде, чем он продолжит свою работу, очевидно.

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

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

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

edit: я только сейчас узнаю, что ядропроблема, тот факт, что web sql API является асинхронным, может иметь решение ... оказывается, есть и синхронная версия API, что-то, чего я не осознавал ... Сейчас я читаю документы, чтобы увидетькак его использовать, но это решило бы проблему хорошо, поскольку единственная причина, по которой providerGet () был написан вообще асинхронно, заключалась в том, чтобы допустить этого поставщика ... код, частью которого является get (), является моим собственным уровнем абстракции над различнымипровайдеры хранения (cookie, web sql, localStorage и т. д.), поэтому наименьший общий знаменатель должен победить, а это означает, что если один асинхронный, они ВСЕ должны быть асинхронными ... единственныйчто было такое веб-SQL ... так что если есть способ сделать это синхронно моя точка становится спорно (еще интересный вопрос в общем, я думаю, хотя)

1012 * edit2: Ну что ж, никакой помощи там не кажется ...похоже, что синхронная версия API не реализована ни в одном браузере, и даже если было указано, что она может быть использована только из рабочих потоков, то, похоже, это не поможет.Хотя, читая некоторые другие вещи, кажется, что есть способ извлечь из этого трюк с помощью рекурсии ... Сейчас я собираю некоторый тестовый код, я опубликую его, если / когда я получу его работу, кажется очень интереснымспособ обойти любую такую ​​ситуацию в общем.

edit3: Согласно моим комментариям ниже, на самом деле нет никакого способа сделать именно то, что я хотел.Решение, с которым я собираюсь решить мою непосредственную проблему, состоит в том, чтобы просто не разрешать использование веб-SQL для хранения данных.Это не идеальное решение, но так как эта спецификация постоянно меняется и, во всяком случае, не получила широкого применения, это не конец света ... надеюсь, что при правильной поддержке синхронная версия будет доступна, и я могу подключить нового провайдера ихорошо идтиВ общем, хотя, похоже, нет никакого способа извлечь из этого чуда ... подтверждает, что я ожидал, что дело, но хотелось бы, чтобы я ошибся в этот раз:)

Ответы [ 5 ]

14 голосов
/ 10 декабря 2011

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

между тем ... функция, которая породила веб-работника, отправляет запрос ajax на тот же веб-сервер используйте синхронный флаг объекта xmlhttprequest (да, у него есть синхронный параметр). поскольку он будет блокироваться до тех пор, пока не завершится http-запрос, вы можете просто заставить свой скрипт веб-сервера опрашивать базу данных на предмет обновлений или чего-либо еще, пока результат не будет отправлен на нее.

Ужасно, я знаю. но он будет блокировать без загрузки процессора: D

в основном

function get(...) {
    spawnWebworker(...);
    var xhr = sendSynchronousXHR(...);
    return xhr.responseTEXT;
}
9 голосов
/ 09 декабря 2011

Нет, вы не можете заблокировать, пока не завершится асинхронный вызов. Это так просто.

Звучит так, что вы, возможно, уже знаете это, но если вы хотите использовать асинхронные вызовы ajax, вам придется реструктурировать способ использования вашего кода. Вы не можете просто иметь метод .get (), который делает асинхронный вызов ajax, блокирует до его завершения и возвращает результат. Шаблон проектирования, который чаще всего используется в этих случаях (например, посмотрите на все API-интерфейсы Google для JavaScript, которые поддерживают работу в сети), заключается в том, чтобы вызывающая сторона передавала вам функцию завершения. Вызов .get() запустит асинхронную операцию и сразу же вернется. Когда операция завершится, будет вызвана функция завершения. Вызывающий должен структурировать свой код соответствующим образом.

Вы просто не можете написать прямой последовательный процедурный код javascript при использовании асинхронных сетей, таких как:

var result = abc.get()
document.write(result);

Наиболее распространенный шаблон дизайна выглядит так:

abc.get(function(result) {
    document.write(result);
});

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


К вашему сведению, более новые браузеры поддерживают концепцию обещаний, которую затем можно использовать с async и await для написания кода, который может выглядеть следующим образом:

async function someFunc() {
    let result = await abc.get()
    document.write(result);
}

Это все еще асинхронно. Это все еще не блокирует. abc.get() должен вернуть обещание, которое разрешается до значения result. Этот код должен находиться внутри функции, которая объявлена ​​async, и другой код вне этой функции будет продолжать выполняться (именно это делает эту неблокирующую). Но вы можете написать код, который «выглядит» больше как блокирующий код, если он локально для конкретной функции, в которой он содержится.

3 голосов
/ 09 декабря 2011

Почему бы просто не передать исходному звонящему свой собственный обратный вызов на get()? Этот обратный вызов будет содержать код, который опирается на ответ.

Метод get() перенаправит обратный вызов на providerGet(), который затем вызовет его, когда вызовет свой собственный обратный вызов.

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

function get( arg1, arg2, fn ) {
    // whatever code

    // call providerGet, passing along the callback
    providerGet( fn );
}

function providerGet( fn ) {
    // do async activity

    // in the callback to the async, invoke the callback and pass it the data
    // ...
          fn( received_data );
    // ...
}

get( 'some_arg', 'another_arg', function( data ) {
    alert( data );
});
1 голос
/ 03 ноября 2013

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

  1. В вашей функции get сериализуйте ваш запрос в строку.
  2. Откройте iframe, передав (A) этот сериализованный запрос и (B) случайное число в строке запроса к этому iframe
    • Ваш iframe имеет некоторый код javascript, который читает запрос SQL и число из егособственная строка запроса
    • Ваш iframe асинхронно начинает выполнение запроса.
    • Когда ваш запрос iframe завершается асинхронно, он отправляет его вместе со случайным числом на ваш сервер, скажем /write.php?rand=###&reslt="blahblahblah"
    • Write.php сохраняет эту информацию где-нибудь
  3. Назад в ваш основной скрипт, послесоздавая и загружая iframe, вы создаете синхронный AJAX-запрос к вашему серверу, скажем, к /read.php?rand=####
  4. / read.php, пока не будет записана информациядоступен, затем возвращает его на главную страницу

В качестве альтернативы, чтобы избежать отправки данных по сети, вместо этого вы можете использовать свой iframe для кодирования результата в сгенерированное холстом изображение, которое кэширует браузер (similсогласно подходу, который, по сообщениям, использовал зомби-куки).Затем ваш сценарий блокировки будет пытаться непрерывно загружать это изображение снова и снова (с некоторой небольшой задержкой, генерируемой сетью при каждом запросе) до тех пор, пока не будет доступна кэшированная версия, которую вы можете распознать с помощью установленного вами флага, указывающего, что это сделано..

1 голос
/ 09 декабря 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...