如何使用Javascript Async-Await作为轮询状态变化的替代方法?

时间:2019-06-02 00:29:02

标签: javascript promise async-await

我想使用Promise来完成以下工作:仅在某种状态准备就绪时才进一步执行。即例如轮询外部状态更改。

我已经尝试过使用Promise和async-await,但是没有得到想要的结果。我在这里做错什么,如何解决?

MDN docs有一些相似之处,但是它们的settimeout在Promise之内被称为-但这并不是我想要的。

我希望console.log显示“现在可以使用此功能了!” 5秒后,但是相反,调用await promiseForState();后似乎停止执行。

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 个答案:

答案 0 :(得分:1)

您在做的是Promise构造函数的执行程序函数在创建Promise时立即执行,然后不再执行。此时,statefalse,所以什么也没发生。

承诺(和async / await)不能代替轮询。您仍然需要在某个地方进行轮询。

好消息:async函数使使用循环和promise进行条件代码变得容易。

但是不要将代码放入promise构造函数执行器函数中,因为它们的错误处理特性很差。它们用于包装legacy code

相反,请尝试以下操作:

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 ");

答案 1 :(得分:0)

我将解决此问题的方法如下。我不确定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需要被stateResolver并重新创建promise。
  2. 我在全球范围内一次创建了承诺,并且总是返回相同的承诺,因为这实际上是每个呼叫者都订阅的一个事件。
  3. 我需要一个res变量来将{{1}}参数从promise构造函数中移出到全局范围内。

答案 2 :(得分:0)

这里是使用.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!');
}

答案 3 :(得分:0)

基于您正在等待来自服务器的消息的评论,您似乎正在尝试解决X/Y problem。因此,我将回答“ 如何等待服务器消息”的问题,而不是等待全局变量更改。

如果您的网络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()事件处理程序分离。您只需要将承诺保存在其他地方。 Evert的答案显示了您可以执行此操作的一种方法。

但是,以我不太谦逊的观点,您不应该这样做。在规模较大的项目中,这将导致难以追踪状态变化发生的来源。这就是我们在90年代和2000年代初使用javascript所做的工作。我们的代码中有很多events,例如onChangeonReadyonData,而不是作为函数参数传递的回调。结果是有时您需要花费很长时间才能弄清楚什么代码触发了什么事件。

回调参数和Promise会强制事件生成器与事件使用者位于代码中的同一位置:

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.
}

您可以从我对另一个问题的回答中进一步了解异步构造函数:Async/Await Class Constructor