Как использовать Javascript Async-Await в качестве альтернативы опросу для смены состояний? - PullRequest
1 голос
/ 02 июня 2019

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

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

Документы MDN имеют что-то похожее, но их время ожидания называется обещанием - хотя это не совсем то, что я ищу.

Я ожидаю, что console.log покажет "Эта функция теперь хороша!" через 5 секунд, но вместо этого выполнение, кажется, останавливается после вызова await promForState ();

var state = false;

function stateReady (){
 state = true;
}


function promiseForState(){
	var msg = "good to go!";
	var promise = new Promise(function (resolve,reject){
        if (state){
            resolve(msg);
        }
    });
  
  return promise;
}

async function waiting (intro){
 
    var result = await promiseForState();
    console.log(intro + result)
  
}
setTimeout(stateReady,5000);
waiting("This function is now ");

Ответы [ 4 ]

1 голос
/ 02 июня 2019

Что вы делаете неправильно, так это то, что функция исполнителя конструктора обещаний выполняется сразу при создании обещания, а затем никогда больше.В этот момент state равно false, поэтому ничего не происходит.

Обещания (и async / await) не являются заменой для опроса.Вам все еще нужно где-то опрашивать.

Хорошая новость: async функции упрощают создание условного кода с циклами и обещаниями.

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

Вместо этого попробуйте следующее:

var state = false;

function stateReady() {
 state = true;
}

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

async function promiseForState() {
  while (!state) {
    await wait(1000);
  }
  return "good to go!";
}

async function waiting(intro) {
    var result = await promiseForState();
    console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
0 голосов
/ 02 июня 2019

На основании ваших комментариев о том, что вы ждете сообщений от сервера, выясняется, что вы пытаетесь решить проблему X / Y .Поэтому я собираюсь ответить на вопрос « как мне ждать сообщений сервера » вместо ожидания изменения глобальной переменной.

Если ваш сетевой API принимает обратный вызов

Множество сетевых API, таких как XMLHttpRequest и узла Http.request () , основаны на обратном вызове.Если используемый вами API является обратным вызовом или основан на событиях, то вы можете сделать что-то вроде этого:

function myFunctionToFetchFromServer () {
    // example is jQuery's ajax but it can easily be replaced with other API
    return new Promise(function (resolve, reject) {
        $.ajax('http://some.server/somewhere', {
            success: resolve,
            error: reject
        });
    });
}

async function waiting (intro){   
    var result = await myFunctionToFetchFromServer();
    console.log(intro + result);
}

Если ваш сетевой API основан на обещаниях

Если, с другой стороны, вы используетеБолее современный сетевой API, основанный на обещаниях, такой как fetch () , вы можете просто дождаться обещания:

function myFunctionToFetchFromServer () {
    return fetch('http://some.server/somewhere');
}

async function waiting (intro){   
    var result = await myFunctionToFetchFromServer();
    console.log(intro + result);
}

Отключение доступа к сети от вашего обработчика событий

Примечаниечто следующее - это только мое мнение , но это также нормальная стандартная практика в сообществе javascript :

В любом случае выше, когда у вас есть обещание, можно отделитьВаш сетевой API формирует ваш waiting() обработчик событий.Вам просто нужно сохранить обещание в другом месте.Ответ Эверта показывает, как вы можете это сделать.

Однако, по моему не столь скромному мнению, вы не должны этого делать .В проектах значительных размеров это приводит к трудностям в поиске источника, где происходит изменение состояния.Это то, что мы сделали в 90-х и начале 2000-х годов с помощью JavaScript.В нашем коде было много events, таких как onChange и onReady или onData вместо обратных вызовов, переданных в качестве параметров функции.В результате иногда требуется много времени, чтобы выяснить, какой код вызывает какое событие.

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

let this_variable_consumes_result_of_a_promise = await generate_a_promise();

this_function_generate_async_event((consume_async_result) => { /* ... */ });

Судя по формулировке вашего вопроса, вы хотите сделать это вместо этого;

.. где-то в вашем коде:

this_function_generate_async_event(() => { set_global_state() });

... где-то ещев вашем коде:

let this_variable_consumes_result_of_a_promise = await global_state();

Я бы посчитал это анти-паттерном .

Вызов асинхронных функций в конструкторах классов

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

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

class MyClass {
    constructor () {
        // constructor logic
    }

    db () {
        if (this.connection) {
            return Promise.resolve(this.connection);
        }
        else {
            return new Promise (function (resolve, reject) {
                createDbConnection(function (error, conn) {
                    if (error) {
                        reject(error);
                    }
                    else {
                        this.connection = conn; // cache the connection
                        resolve(this.connection);
                    }
                });
            });
        }
    }
}

Использование:

const myObj = new MyClass();

async function waiting (intro){   
    const db = await myObj.db();
    db.doSomething(); // you can now use the database connection.
}

Подробнее об асинхронных конструкторах можно прочитать в моем ответе на этот другой вопрос: Конструктор асинхронных / ожидающих классов

0 голосов
/ 02 июня 2019

Вот альтернативный вариант использования .requestAnimationFrame().

Предоставляет простой и понятный интерфейс.

var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);

// continue until serverStuffComplete is true
function waitForServer(now) {
  if (serverStuffComplete) {
    doSomethingElse();
  } else {
    // place this request on the next tick
    requestAnimationFrame(waitForServer);
  }
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);

//resolve the promise or whatever
function doSomethingElse() {
  console.log('Done baby!');
}
0 голосов
/ 02 июня 2019

Я бы решил это следующим образом.Я не уверен на 100%, что это решит вашу проблему, но при этом предполагается, что у вас есть контроль над stateReady().

let state = false;
let stateResolver;

const statePromise = new Promise( (res, rej) => {

  stateResolver = res;

});


function stateReady(){
 state = true;
 stateResolver();
}


async function promiseForState(){
   await stateResolver();
   const msg = "good to go!";
   return msg;
}

async function waiting (intro){

    const result = await promiseForState();
    console.log(intro + result)

}
setTimeout(stateReady,5000);
waiting("This function is now ");

Некоторые ключевые моменты:

  1. То, как этов настоящее время написано, что «состояние» может переходить к true только один раз.Если вы хотите, чтобы это срабатывало много раз, некоторые из этих const должны будут иметь значение let, а обещание необходимо создать заново.
  2. Я создал обещание один раз, глобально и всегдавозвращать то же самое, потому что на самом деле это только одно событие, на которое подписывается каждый вызывающий объект.
  3. Мне понадобилась переменная stateResolver, чтобы поднять аргумент res из конструктора обещаний в глобальную область.
...