为什么我的代码按执行顺序执行

时间:2019-02-12 08:03:41

标签: javascript promise order-of-execution

我正在尝试使用promise来确保设置某些属性,然后再尝试使用这些变量。

我已经设法编写了一些代码,这些代码似乎可以实现我想要的功能,但鉴于我对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

我想我只是不明白我在这里构造的东西。 主要是它的时机让我感到困惑。 为什么它按照我的期望却以不同的顺序执行?

编辑: 我完成了向API回调中添加promise函数包装器的工作,并使用Promise.all()来解决它们,然后仅在then中呈现页面。基本上,它满足了我现在想要的。

使用诺言时我学到了两件事:

  1. 如果您有函数包装器,则实际上甚至可以在promise.all()调用内向其传递参数

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

  3. 的数组中

全部承诺

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

2 个答案:

答案 0 :(得分:0)

我认为这是正在发生的事情。 首先,您执行forEachforEach的回调将对回调的每个调用(将被调用多次)将任务排队(待当前脚本完成后由事件循环执行)您在characters数组中具有的元素。将这些任务排队后,脚本的执行将继续执行console.log(characters);,这是您首先看到的内容,现在脚本执行完成,事件循环将处理任务队列,处理forEach添加的第一个任务(这是函数skillPromise的一次执行,这会向微任务队列添加一个承诺。并且它为forEach循环添加的每个任务执行此操作。如果在处理这些任务时某些承诺得到解决,则javascript将在处理forEach排队的当前任务后处理它们(不是一个承诺,而是所有已解决的承诺)。如果在处理{{ 1}} javascript将在解决所有问题后对其进行处理。

顺序是,首先执行forEach,然后处理任务和承诺。

您可以查看this视频,我发现它很好地解释了JavaScript事件循环的工作原理。

答案 1 :(得分:-1)

您可以使用.reduce()函数包装上一个承诺的then()中的每个承诺。

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