Вызовите функцию синхронного асинхронного Javascript

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

Хорошо, это из-за этого, как я могу сделать это, чтобы я мог:

function doSomething() {

  var data;

  function callBack(d) {
    data = d;
  }

  myAsynchronousCall(param1, callBack);

  // block here and return data when the callback is finished
  return data;
}

В примерах (или их отсутствии) используются библиотеки и/или компиляторы, обе из которых не являются жизнеспособными для этого решения. Мне нужен конкретный пример того, как сделать блок (например, НЕ оставлять функцию doSomething до вызова обратного вызова) БЕЗ замораживания пользовательского интерфейса. Если такое возможно в JS.

Ответ 1

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

OK. но вы действительно должны сделать это правильно... или что-то еще

"Мне нужен конкретный пример того, как его блокировать... БЕЗ замораживания пользовательского интерфейса. Если такое возможно в JS".

Нет, невозможно заблокировать исполняемый JavaScript без блокировки пользовательского интерфейса.

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

function doSomething() {

      // callback sets the received data to a global var
  function callBack(d) {
      window.data = d;
  }
      // start the async
  myAsynchronousCall(param1, callBack);

}

  // start the function
doSomething();

  // make sure the global is clear
window.data = null

  // start polling at an interval until the data is found at the global
var intvl = setInterval(function() {
    if (window.data) { 
        clearInterval(intvl);
        console.log(data);
    }
}, 100);

Все это предполагает, что вы можете изменить doSomething(). Я не знаю, было ли это в карточках.

Если он может быть изменен, то я не знаю, почему вы не просто передали обратный вызов doSomething() для вызова из другого обратного вызова, но я лучше остановился, прежде чем попаду в беду.;)


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

function doSomething( func ) {

  function callBack(d) {
    func( d );
  }

  myAsynchronousCall(param1, callBack);

}

doSomething(function(data) {
    console.log(data);
});

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

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

myAsynchronousCall(param1, func);

Ответ 3

Асинхронные функции, функция в ES2017, делают асинхронный код синхронизированным с помощью обещаний (особая форма асинхронного кода) и ключевого слова await. Также обратите внимание на примеры кода ниже ключевого слова async перед ключевым словом function которое обозначает функцию async/await. Ключевое слово await не будет работать без функции, предварительно фиксированной с ключевым словом async. Поскольку в настоящее время нет исключения из этого, это означает, что никакие ожидания верхнего уровня не будут работать (верхний уровень ожидает, что означает ожидание вне какой-либо функции). Хотя есть предложение на высшем уровне, await.

ES2017 был утвержден (то есть окончательно доработан) как стандарт для JavaScript 27 июня 2017 года. Async await может уже работать в вашем браузере, но если нет, вы все равно можете использовать эту функциональность с помощью транспортера javascript, такого как babel или traceur. Chrome 55 имеет полную поддержку асинхронных функций. Так что если у вас более новый браузер, вы можете попробовать код ниже.

См. Таблицу совместимости kangax es2017 для совместимости браузера.

Вот пример функции async await под названием doAsync которая берет три паузы в одну секунду и печатает разницу во времени после каждой паузы от времени начала:

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

async function doAsync () {
  var start = Date.now(), time;
  console.log(0);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
}

doAsync();

Ответ 4

В http://taskjs.org/

есть одно приятное обходное решение.

Он использует генераторы, которые являются новыми для javascript. Поэтому в настоящее время он не реализуется большинством браузеров. Я тестировал его в firefox, и для меня это хороший способ обернуть асинхронную функцию.

Вот пример кода из проекта GitHub

var { Deferred } = task;

spawn(function() {
    out.innerHTML = "reading...\n";
    try {
        var d = yield read("read.html");
        alert(d.responseText.length);
    } catch (e) {
        e.stack.split(/\n/).forEach(function(line) { console.log(line) });
        console.log("");
        out.innerHTML = "error: " + e;
    }

});

function read(url, method) {
    method = method || "GET";
    var xhr = new XMLHttpRequest();
    var deferred = new Deferred();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status >= 400) {
                var e = new Error(xhr.statusText);
                e.status = xhr.status;
                deferred.reject(e);
            } else {
                deferred.resolve({
                    responseText: xhr.responseText
                });
            }
        }
    };
    xhr.open(method, url, true);
    xhr.send();
    return deferred.promise;
}

Ответ 5

Вы можете заставить асинхронный JavaScript в NodeJS быть синхронным с sync-rpc.

Тем не менее, он определенно заморозит ваш пользовательский интерфейс, поэтому я все еще скептически отношусь к тому, нужно ли вам использовать ярлык. Невозможно приостановить единый поток в JavaScript, даже если NodeJS позволяет вам иногда блокировать его. Никакие обратные вызовы, события или что-либо асинхронное вообще не смогут обрабатываться, пока ваше обещание не разрешится. Поэтому, если вы, читатель, не столкнетесь с неизбежной ситуацией, такой как OP (или, в моем случае, не пишете прославленный скрипт оболочки без обратных вызовов, событий и т.д.), НЕ ДЕЛАЙТЕ ЭТОГО!

Но вот как вы можете сделать это:

./calling-file.js

var createClient = require('sync-rpc');
var mySynchronousCall = createClient(require.resolve('./my-asynchronous-call'), 'init data');

var param1 = 'test data'
var data = mySynchronousCall(param1);
console.log(data); // prints: received "test data" after "init data"

./my-asynchronous-call.js

function init(initData) {
  return function(param1) {
    // Return a promise here and the resulting rpc client will be synchronous
    return Promise.resolve('received "' + param1 + '" after "' + initData + '"');
  };
}
module.exports = init;

ОГРАНИЧЕНИЯ:

Оба они являются следствием того, как реализован sync-rpc, то есть злоупотребляя require('child_process').spawnSync:

  1. Это не будет работать в браузере.
  2. Аргументы вашей функции должны быть сериализуемыми. Ваши аргументы будут JSON.stringify и выходить из JSON.stringify, поэтому функции и неперечислимые свойства, такие как цепочки прототипов, будут потеряны.

Ответ 6

Вот обертка, которая может помочь тем, кто ищет код с минимальными усилиями:

async function makeSyncAsync() {  
    return Promise.resolve().then(async function(){
        var result = await new Promise(resolve => {
            // Modify below with the nature of your async function
            // and assure resolve is in your async function callback and gives the value that you want to return
            setTimeout(() => {
                resolve('Correct Value');
            }, 2000)
        });
        return result;
    });
}

Здесь код setTimeout обернут. И при вызове makeSyncAsync мы должны использовать " àwait'''and in order to do that we need to define our caller function as синхронизировать". (Может быть, именно эта необходимость делает все немного сложнее.)

Ссылка здесь.

Ответ 7

Используйте Async Await и Promise.resolve/Promise.all 😊

Ответ 8

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

Приведенный ниже код возможен, если ваша среда выполнения поддерживает спецификацию ES6.

Подробнее об асинхронных функциях

async function myAsynchronousCall(param1) {
    // logic for myAsynchronous call
    return d;
}

function doSomething() {

  var data = await myAsynchronousCall(param1); //'blocks' here until the async call is finished
  return data;
}