Ожидание возвращает ожидающие обещания в Typescript - PullRequest
0 голосов
/ 28 августа 2018

Я - разработчик .Net, новичок в мире javascript / node и испытываю некоторые затруднения с выполнением запроса на получение 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.html, вызывая метод this.makeRequest (), я получаю Promise { pending } возвращаемое значение. Я не уверен, почему это так. Из того, что я исследовал, не ждет, дождаться обещания, которое будет выполнено? Я тоже пробовал с синтаксисом .then, но получаю еще более странный результат: Promise {_bitField: 0, _fulfillmentHandler0: undefined, _rejectionHandler0: undefined, _promise0: undefined, _receiver0: undefined, …}. Версия 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 для установки свойства. Мой код ниже. Я также попытался с и без ключевых слов async / await в методе getRequest. В любом случае, моя собственность теперь возвращается как 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

Итак, не зная, что делать, я решил снова попробовать маршрут обещаний с модулем запроса вместо запроса-обещания. В моей функции makeRequest() я возвращаю новое обещание, которое оборачивает мой запрос, а затем вызываю .then() для него в методе getHtml(). Следует также отметить, что я тестирую этот код с помощью модульных тестов mocha. Не уверен, что это как-то связано с этим. Я тоже пытался сделать тестовый асинхронный тест и использовать await, но без сигары. Ниже приведены мой класс и тест.

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

Последнее замечание. Я вставил console.log(body); в метод getHtml (), чтобы увидеть, вызывается ли он. Когда я запускаю модульный тест и ставлю точку останова где-нибудь в тесте, она никогда не вызывается, даже если я создал экземпляр своего класса. Однако, когда я продолжаю выполнение и заканчиваю тест, он печатает весь 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 (также пытался дождаться создания экземпляра), и снова не было сигары.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Мне не ясно, что ваш класс пытается моделировать. Пара замечаний хоть. Если ваш класс моделирует какую-то ценность и пытается скрыть, как это значение, полученное / созданное внутри его частных функций, оно должно сделать его потребители знают, что это «асинхронное» значение. Это означает, что, хотя ваша программа компилируется, потому что this.html назначенный внутри асинхронной функции, он фактически недоступен до определенное событие (разрешение запроса в этом примере). Потребитель, с точки зрения типа это никак не узнать.

Решением было бы просто присвоить обещание this.html, как

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

Используя это, любой потребитель теперь знает, что, создавая экземпляр этого класс возвращается объект, который имеет html поле, содержащее обещание разрешение на то, что вы хотите, чтобы HTML. Практически это означает, что любой потребитель этого класса будет сильно рекомендуется использовать его в контексте async:

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

В заключение вы просто подняли асинхронный контекст на уровне потребителя. уровня производителя.

0 голосов
/ 30 августа 2018

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

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);
    });
});
0 голосов
/ 28 августа 2018

Когда я устанавливаю переменную this.html, вызывая метод this.makeRequest (), я получаю Promise {pending}. Я не уверен, почему это так.

Потому что async функции возвращают обещание.

Fix

Переместите асинхронные функции вызова из других асинхронных функций (не из конструктора) и используйте await.

...