在打字稿中等待 returns 个未决承诺

Await returns pending promises in Typescript

我是一名刚接触 javascript/node 世界的 .Net 开发人员,在使用请求承诺从网页发出获取 HTML 的请求时遇到了一些麻烦。我尝试了以下代码的变体。

import cheerio = require('cheerio');
import request = require('request-promise');

export class Address {
    public html;
    public $: CheerioStatic;

    constructor() {
         this.html = this.makeRequest();
    }

    private async makeRequest() {
        const options = {
            uri: 'https://www.google.com/',
            transform(body) {
                return cheerio.load(body);
            }
        };
        return await request(options);
    }
}

问题是,当我通过调用 this.makeRequest() 方法设置 this.html 变量时,我得到 Promise { pending } 返回。我不确定这是为什么。从我一直在研究的内容来看,不是等待,等待承诺得到解决吗?我也尝试过使用 .then 语法,但我得到了一个更奇怪的结果: Promise {_bitField: 0, _fulfillmentHandler0: undefined, _rejectionHandler0: undefined, _promise0: undefined, _receiver0: undefined, …}。 returns 那个奇怪的承诺低于 makeRequest() 的版本。

private makeRequest() {
    const options = {
        uri: 'https://www.google.com/',
        transform(body) {
            return cheerio.load(body);
        }
    };
    return request(options)
        .then(function($: CheerioStatic) {
            return $;
        })
        .catch(function(err) {
            //
        });
}

我想坚持使用 async/await 语法,但如果您能帮助我解释为什么我的代码无法运行以及我可以做些什么来修复它,我们将不胜感激!

更新

根据@basarat 的建议,我使用 await 关键字创建了另一个异步方法来设置 属性。我的代码如下。我还尝试在 getRequest 方法中使用和不使用 async/await 关键字。不管怎样,我的 属性 现在返回为 undefined。不确定这一步是否正确。

import cheerio = require('cheerio');
import request = require('request-promise');

export class Address {
    public html;
    public $: CheerioStatic;

    constructor() {
        this.getHtml();

        // this.parseHtml();
    }

    private async makeRequest() {
        const options = {
            uri: 'https://www.google.com/',
            transform(body) {
                return cheerio.load(body);
            }
        };
        return await request(options);
    }

    private async getHtml() {
        this.html = await this.makeRequest();
    }
}

更新 2

所以,不知道该怎么做,我决定再次尝试 promises 路线,使用请求模块而不是请求承诺。在我的 makeRequest() 函数中,我返回一个包装我的请求的新承诺,然后在 getHtml() 方法中对其调用 .then()。另一件需要注意的事情是,我正在通过 mocha 单元测试来测试这段代码。不确定这是否与它有关。我试过使测试异步并在那里使用等待,但没有雪茄。下面是我的 class 和测试。

import request = require('request');

export class Address {
    public html;

    constructor() {
        this.html = this.getHtml();
    }

    public getHtml() {
        this.makeRequest().then((body) => {
            console.log(body);
            return body;
        });
    }

    private makeRequest() {
        return new Promise(function(resolve, reject) {
            request('https://www.google.com/', function(error, response, body) {
                if (error) {
                    reject(error);
                }
                resolve(body);
            });
        });
    }
}

最后一次观察。我在 getHtml() 方法中放入 console.log(body); 以查看它是否被调用。当我 运行 单元测试并在测试中的任何地方放置一个断点时,即使我已经创建了我的 class 的实例,也永远不会调用它。但是,当我继续执行并完成测试时,它会打印出所有 HTML!所以对我来说,最近的代码似乎大部分都很好,但可能存在某种时间问题。由于 HTML 仍会打印出来,因此至少正在拨打电话,但没有打到我的 属性。下面是我正在执行的测试。

describe('Address', () => {
    // const address = new Address();
    it('is not empty', () => {
        const address = new Address();
        const ad = address.html;
        // console.log(ad);
    });
});

此外,在测试中,我尝试使 it 语句异步并将 await 添加到 address.html (也尝试等待实例化),并且再次没有雪茄。

when I set the this.html variable by calling the this.makeRequest() method I get Promise { pending } returned. I'm not sure why this is.

因为 async 实现了 return 一个承诺。

修复

从其他异步函数(不是构造函数)移动调用异步函数并使用 await。

我不清楚你的 class 试图建模什么。几点说明 尽管。 如果您的 class 正在对某种价值建模并试图隐藏它是如何 value obtained/created 在它的私有函数中它应该使它的 消费者意识到这是一个 "Async" 值。 这意味着尽管您的程序可以编译,因为 this.html 是 在异步函数内部赋值,实际上在调用之前不可用 特定事件(本例中请求的解析)。消费者, 从类型的角度无法知道这一点。

一个解决方案是简单地向 this.html 分配一个承诺,例如

class Adress {
  html: Promise<theRealTypeOfHtml>
  ...
  constructor () {
      this.getHtml();
  }
  // no more async
  private getHtml () {
    this.html = this.makeRequest()
  }
}

使用这个,任何消费者现在都知道通过创建这个实例 class 它返回一个对象,该对象有一个包含承诺的 html 字段 解决任何你想要 html 的事情。 实际上,这意味着此 class 的任何消费者都会强烈 鼓励在 async 上下文中使用它:

async function main () {
  const anAdress = new Adress();
  const html = await anAdress.html;
}

总而言之,您只是在消费者级别解除了异步上下文 生产者级别。

终于找到解决方法了!我基本上创建了一个调用请求方法的异步函数,然后必须进行一个等待我的异步方法调用的异步测试。总而言之,似乎我必须创建一个方法来实例化所有内容,而不能在构造函数中这样做。如果有人能阐明这是为什么,那就太棒了,因为这对我来说似乎真的违反直觉。我的意思是,这不是构造函数的用途吗?也许,如果我在任何地方都找不到它,我会 post 另一个关于它的问题。无论如何,我的代码在下面。

import request = require('request');

export class Address {
    public html;

    constructor() {
        //
    }

    public async getHtml() {
        return await this.makeRequest()
            .then((body) => {
                this.html = body;
            });
    }

    public makeRequest() {
        return new Promise(function(resolve, reject) {
            request('https://fakena.me/random-real-address/', function(error, response, body) {
                if (error) {
                    reject(error);
                }
                resolve(body);
            });
        });
    }
}

这是我的测试方法。

describe('Address', () => {
    // const address = new Address();
    it('is not empty', async () => {
        const address = new Address();
        await address.getHtml();
        console.log(address.html);
    });
});