1

我正在尝试使用承诺来确保在稍后尝试使用这些变量之前设置某些属性。

我已经设法拼凑出一些代码,这些代码似乎可以做我想做的事,但考虑到我对 Promise 的了解,这些代码的顺序并不符合我的预期。

创建 promise 函数包装器(我认为)

function skillPromise(char, opts) {
    return new Promise((res, rej) => {
        skills_api.getCharactersCharacterIdSkills(char.eve_id, opts, (e, r, b) => {
            if (!e) res(r.total_sp);
            else rej(e);
        });
    });
}

使用承诺

characters.forEach((c) => {
    skillPromise(c, opts).then((spt) => {
        c.totalSP = spt;
        console.log(spt);
        console.log(c.totalSP);
    }).catch(() => {});
});
console.log(characters);

输出

第一次运行

[ { eve_id: 123456,
    name: 'name',
    access_token: '',
    refresh_token: '' } ]
4652555
4652555

第二轮

[ { eve_id: 123456,
    name: 'name',
    access_token: '',
    refresh_token: '',
    totalSP: 4652555
  },
  { eve_id: 654321,
    name: 'eman',
    access_token: '',
    refresh_token: '' } ]
9075602
9075602

所有后续运行都将遵循该模式。

强制刷新与添加登录具有相同的效果,但也会重新打印之前的值。

[ { eve_id: 123456,
    name: 'name',
    access_token:'',
    refresh_token:'',
    totalSP: 4652555 },
  { eve_id: 654321,
    name: 'eman',
    access_token:'',
    refresh_token:'',
    totalSP: 9075602 } ]
9075602
9075602

我想我只是不明白我在这里构建了什么。主要是它让我感到困惑的时机。为什么它会按照我的预期做,但顺序不同?

编辑:我完成了将 promise 函数包装器添加到 API 回调并用于Promise.all()解析它们,然后只在 then 中呈现页面。基本上,它做了我现在想要的。

我在使用 Promise all 时学到的两件事:

  1. promise.all()如果你有函数包装器,你甚至可以在调用内部将参数传递给它们

  2. 您仍然可以返回返回,但它们位于传递给.then()

答应所有

Promise.all([skillPromise(c, opts), walletPromise(c, opts), locationPromise(c, opts)]).then((returns) => {
    c.totalSP = returns[0];
    c.walletBalance = returns[1];
    c.solarSystem = returns[2];
    res.render('characters', { characters: characters });
}).catch(() => {});

Promise 函数包装器:

function skillPromise(char, opts) {
    return new Promise((res, rej) => {
        skills_api.getCharactersCharacterIdSkills(char.eve_id, opts, (e, r, b) => {
            if (!e) res(r.total_sp);
            else rej(e);
        });
    });
}

function walletPromise(char, opts) {
    return new Promise((res, rej) => {
        wallet_api.getCharactersCharacterIdWallet(char.eve_id, opts, (e, r, b) => {
            if (!e) res(r);
            else rej(e);
        });
    });
}

function locationPromise(char, opts) {
    return new Promise((res, rej) => {
        location_api.getCharactersCharacterIdLocation(char.eve_id, opts, (e, r, b) => {
            if (!e) {
                // Translate solar_system_id & station_id into System name and Station Name
                universe_api.getUniverseSystemsSystemId(r.solar_system_id, opts, (e, r, b) => {
                    if (!e) res(r.name);
                    else rej(e);
                });
            } else rej(e);
        });
    });
}
4

2 回答 2

0

我认为这就是正在发生的事情。首先,您执行forEach,forEach的回调为每次调用回调(将被调用的次数与字符数组中的元素一样多次)排队一个任务(在当前脚本完成后由事件循环稍后执行)。在排队这些tasks 脚本的执行继续执行,这console.log(characters);就是您首先看到的打印。现在脚本执行完成,事件循环开始处理任务队列,它处理由它向微任务队列添加了一个承诺。它为每个任务执行此操作forEachskillPromiseforEach循环添加。如果在处理这些任务时,某些承诺得到解决,javascript 将在处理由forEach. 如果在处理所有由 javascript 排队的任务之前没有解决任何承诺,forEach则在解决后将处理所有这些任务。

所以顺序是,首先我们执行console.log(characters);,然后我们处理任务和承诺。

你可以查看这个视频,我发现它很好地解释了 javascript 的事件循环是如何工作的。

于 2019-02-12T09:34:08.883 回答
-1

您可以使用.reduce()函数将每个 Promise 包装在then()之前的 Promise 中。

characters.reduce((prevPromise, c) => {
    var promise = skillPromise(c, opts).then((spt) => {
            c.totalSP = spt;
            console.log(spt);
            console.log(c.totalSP);
        }).catch(() => {});

    if(prevPromise == null){
        return promise;
    }else{
        prevPromise.then(promise)
    }
}, null);
于 2019-02-12T08:25:53.487 回答