【问题标题】:Using async/await to make sequential (blocking calls) returns too early使用 async/await 进行顺序(阻塞调用)返回过早
【发布时间】:2018-11-03 02:14:26
【问题描述】:

下面是一个简单的例子来说明我为什么要尝试使用 fetch API。我希望 async fetchAsync() 会阻塞,直到它返回数据(或异常),但输出显示它没有。

constructor entering fetchAsync... ** we are here!!! ** leaving fetchAsync. finish initialization

当我的对象使用文件内容完成初始化时,我一直试图弄清楚如何在finish initialization 之后显示完成字符串 (we are here)。 await/async 不应该在完成之前阻塞吗?

class A {
    filename = "./resources/test.json";

    constructor() {
        console.log("constructor");
        this.fetchAsync(this.filename)
            .then( data => this.initialize(data)
            ).catch(reason => console.log(reason.message))
    }

    async fetchAsync(filename) {
        console.log("entering fetchAsync...");
        let data = await (await fetch(filename)).json();
        console.log("leaving fetchAsync.");
        return data;
    }

    initialize() {
        setTimeout(() => console.log("finish initialization"), 1000)
    }
}

let a = new A();
console.log("*** we are here!!! ***");

【问题讨论】:

    标签: javascript asynchronous async-await es6-promise


    【解决方案1】:

    您一定忘记了asynchronous 的含义:

    class A {
      constructor() {
          this.filename = "./resources/test.json";
          console.log("constructor");
          this.fetchAsync(this.filename)
              .then( data => this.initialize(data)
              ).catch(reason => console.log(reason.message))
      }
    
      async fetchAsync(filename){
          console.log("entering fetchAsync...");
          let data = await fetch(filename);
          console.log("leaving fetchAsync.");
          return data.json;
      }
      initialize() {
          setTimeout(() => {console.log("finish initialization"); console.log("*** we are here!!! ***"}, 1000)
      }
    }
    let a = new A();
    

    【讨论】:

      【解决方案2】:

      await 不会阻塞 - 这对用户和应用程序非常不友好 - 它只是等待承诺解决后再继续,并且不能在顶层完成(然而)。如果您希望 we are here 仅在初始化完成后显示,那么您需要能够访问在初始化完成后解析的 Promise,然后对其调用 then

      您还应该确保 initalize 返回一个 Promise,以便它可以链接到 a 上的外部调用。

      所以,你可以试试这样的:

      const dummyRequest = () => new Promise(res => setTimeout(res, 1000, 'response'));
      
      class A {
        // filename = "./resources/test.json";
        constructor() {
          console.log("constructor");
        }
        startFetch() {
          return this.fetchAsync(this.filename || 'foo')
            .then(data => this.initialize(data)).catch(reason => console.log(reason.message))
        }
        async fetchAsync(filename) {
          console.log("entering fetchAsync...");
          // let data = await (await fetch(filename)).json();
          const data = await dummyRequest();
          console.log("leaving fetchAsync.");
          return data;
        }
      
        initialize() {
          return new Promise(resolve => {
            setTimeout(() => {
              console.log("finish initialization");
              resolve();
            }, 1000);
          });
        }
      }
      
      const a = new A();
      a.startFetch()
        .then(() => {
          console.log("*** we are here!!! ***");
        });

      构造函数不能异步,这就是我创建startFetch 函数的原因。

      【讨论】:

      • 感谢您的出色回答。我不知道对构造函数的限制。我也不认为顶级等待是一个好主意。以这种方式编写的代码的唯一警告是,我的其余代码(在初始化完成后)应该从那时起调用? init().then(the_rest_of the application) 之类的东西似乎不太合适
      • 无论如何,更大的代码块应该在它们自己的独立函数中。要在此处应用它,您可以:a.startFetch().then(main); ... function main() { /* code here */ } 使用异步代码,没有办法绕过至少 一个 行缩进。
      • 最后一个问题,如果您不介意的话:如果我在 setTimeout() 调用(或任何其他进行大量计算的方法)之前添加 await,我是否也可以使 initialize() 异步。 .
      • 当然,initialize 已经是异步的(在我的 sn-p 中),我假设 setTimeout 只是其他东西的替代品。 (但请注意,Promises 的异步性与同步繁重计算不同)你不能 await 一个 setTimeout,你只能 await 一个 Promise
      • 明白了。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      • 2013-12-25
      • 2016-02-08
      • 2018-07-24
      相关资源
      最近更新 更多