承诺未按预期解决

Promises not resolving as expected

我很难思考如何实现我的承诺。在我的主代码块中,我使用:

this.services.city.get(this.game.player.id, this.game.player.realm).then((cities) => {
    console.log('core', cities);
}).catch((err) => console.log(err));

以及我的城市服务中的获取方法:

get(owner, realm){
    var _t = this;
    return new Promise(function(resolve, reject){
        var cities = [];
        var query = 'SELECT * FROM cities WHERE owner = '+owner+' AND realm = '+realm;
        _t.core.db.query(query).then((results) => {
            for(var i = 0; i < results.length; i++){
                var data = {
                    id: results[i].id,
                    name: results[i].name,
                    owner: results[i].owner,
                    age: results[i].age,
                    x: results[i].x,
                    y: results[i].y,
                    realm: results[i].realm,
                    food: results[i].food,
                    wood: results[i].wood,
                    stone: results[i].stone,
                    ore: results[i].ore,
                    gold: results[i].gold,
                    population: results[i].population,
                    buildings: {}
                };
                var city = CityModel(data);
                Promise.all([_t.getCurrentBuilds(city.id), _t.getCityBuildings(city.id), _t.getFieldBuildings(city.id)]).then((values) => {
                    city.currentBuilds = values[0];
                    city.buildings.city = values[1];
                    city.buildings.field = values[2];
                    console.log('city', city);
                    cities.push(city);
                });
            }
            resolve(cities);
        }).catch((err) => {
            reject(err);
        });
    });
}

console.log('city', city); 的输出显示了我需要的数据,但是,console.log('core', cities); 是一个空数组。在解析整个城市数组之前,我如何才能等待 get 方法的循环完成以及其他 promises 解析?

在调用 resolve(cities) 之前,您没有等待 Promise.all() 完成。这里有很多错误。您正在使用反模式将所有这些包装在另一个手动创建的承诺中(您可以只使用已经拥有的承诺)并且您需要将 Promise.all() 链接到父级承诺。并且,您需要将循环承诺累积在一个数组中并对它们使用 Promise.all() 。或者,更改为顺序实现并使用 async/await.

这是修复后的版本:

get(owner, realm) {
    const _t = this;
    const query = 'SELECT * FROM cities WHERE owner = ' + owner + ' AND realm = ' + realm;
    return _t.core.db.query(query).then((results) => {
        const promises = [];
        for (let result of results) {
            const data = {
                id: result.id,
                name: result.name,
                owner: result.owner,
                age: result.age,
                x: result.x,
                y: result.y,
                realm: result.realm,
                food: result.food,
                wood: result.wood,
                stone: result.stone,
                ore: result.ore,
                gold: result.gold,
                population: result.population,
                buildings: {}
            };
            const city = CityModel(data);
            const p = Promise.all([
                _t.getCurrentBuilds(city.id),
                _t.getCityBuildings(city.id),
                _t.getFieldBuildings(city.id)
            ]).then((values) => {
                city.currentBuilds = values[0];
                city.buildings.city = values[1];
                city.buildings.field = values[2];
                console.log('city', city);
                return city;
            });
            promises.push(p);
        }
        // collect all the city objects from their promises
        return Promise.all(promises);
    });
}

由于我无法 run/test 此代码,因此此处可能存在一些拼写错误,但希望您能看到核心结构并进行必要的修复。

主要变化:

  1. Return 函数中的顶级承诺并将其他所有内容链接到它上面,以消除将现有承诺包装在手动创建的承诺中的反模式。除了使异步流程正常工作之外,您的代码还缺少各种无法将错误传播回调用者的错误处理路径。
  2. for 循环中 Promise.all() 的结果收集到一个数组中,这样我们就可以在该数组上使用 Promise.all() 来知道什么时候一切都完成了。
  3. 在内部 Promise.all() 中执行 return city 以便它成为该承诺的解析值,我们可以将其作为 Promise.all() 结果的一部分按顺序进行跟踪,而不是将其推入更高范围的数组。
  4. 添加 Promise.all() 以跟踪 for 循环的结果并从 .then() 处理程序中收集所有城市和 return 以链接它。
  5. 处处摆脱var。你不应该再用 var 编程了。 constlet 在现代 Javascript.
  6. 更合适

这里有一个更简单的版本,它使用了一些 async/await,但仍然保留了各个城市查询的并行执行:

async get(owner, realm) {
    const _t = this;
    const query = 'SELECT * FROM cities WHERE owner = ' + owner + ' AND realm = ' + realm;
    const results = await _t.core.db.query(query);
    return Promise.all(results.map(async result => {
        const data = {
            id: result.id,
            name: result.name,
            owner: result.owner,
            age: result.age,
            x: result.x,
            y: result.y,
            realm: result.realm,
            food: result.food,
            wood: result.wood,
            stone: result.stone,
            ore: result.ore,
            gold: result.gold,
            population: result.population,
            buildings: {}
        };
        const city = CityModel(data);
        const values = await Promise.all([
            _t.getCurrentBuilds(city.id),
            _t.getCityBuildings(city.id),
            _t.getFieldBuildings(city.id)
        ]);
        city.currentBuilds = values[0];
        city.buildings.city = values[1];
        city.buildings.field = values[2];
        console.log('city', city);
        return city;
    });
}