【问题标题】:JS - Promises executing in the wrong orderJS - 以错误的顺序执行的承诺
【发布时间】:2017-11-29 23:00:37
【问题描述】:

我的代码试图做的是创建一个具有一些动态属性的对象数组,这些属性将作为某些函数的结果来填充。我正在尝试使用 Promise,否则我的模板会在函数完成之前呈现,并且这些对象的属性将为 null 或未定义,从而导致模板出错。

这是第一个函数

fetchUserPortfolioCoins({ commit, dispatch, state, rootGetters }) {
        const promises = []
        promises.push(dispatch('utilities/setLoading', true, { root: true })) // start loader
        if (!rootGetters['auth/isAuthenticated']) {
            // if user isn't logged, pass whatever is in the store, so apiDetails will be added to each coin
            let coins = state.userPortfolioCoins
            coins.forEach(coin => { promises.push(dispatch('createAcqCostConverted', coin)) })
            commit('SET_USER_COINS', { coins, list: 'userPortfolioCoins' })
        } else {
            // otherwise, pass the response from a call to the DB coins
            Vue.axios.get('/api/coins/').then(response => {
                let coins = response.data
                coins.forEach(coin => { promises.push(dispatch('createAcqCostConverted', coin)) })
                commit('SET_USER_COINS', { coins, list: 'userPortfolioCoins' })
            })
        }
        Promise.all(promises)
            .then(() => {
                commit('SET_USER_PORTFOLIO_OVERVIEW')
                dispatch('utilities/setLoading', false, { root: true })
            })
            .catch(err => { console.log(err) })
    },

那叫这个:

createAcqCostConverted({ dispatch, rootState }, coin) {
    const promises = []
    // this check is only going to happen for sold coins, we are adding sell_price_converted in case user sold in BTC or ETH
    if (coin.sell_currency === 'BTC' || coin.sell_currency === 'ETH') {
        const URL = `https://min-api.cryptocompare.com/data/pricehistorical?fsym=${coin.coin_symbol}&tsyms=${rootState.fiatCurrencies.selectedFiatCurrencyCode}&ts=${coin.sold_on_ts}`
        promises.push(Vue.axios.get(URL, {
            transformRequest: [(data, headers) => {
                delete headers.common.Authorization
                return data
            }]
        }))
    }
    // if user bought with BTC or ETH we convert the acquisition cost to the currently select fiat currency, using the timestamp
    if (coin.buy_currency === 'BTC' || coin.buy_currency === 'ETH') {
        const URL = `https://min-api.cryptocompare.com/data/pricehistorical?fsym=${coin.coin_symbol}&tsyms=${rootState.fiatCurrencies.selectedFiatCurrencyCode}&ts=${coin.bought_on_ts}`
        promises.push(Vue.axios.get(URL, {
            transformRequest: [(data, headers) => {
                delete headers.common.Authorization
                return data
            }]
        }))
    } else {
        // if the selected fiatCurrency is the same as the buy_currency we skip the conversion
        if (coin.buy_currency === rootState.fiatCurrencies.selectedFiatCurrencyCode) {
            coin.acquisition_cost_converted = NaN
            return coin
            // otherwise we create the acq cost converted property
        } else promises.push(dispatch('fiatCurrencies/convertToFiatCurrency', coin, { root: true }))
    }
    Promise.all(promises)
        .then(response => {
            const value = response[0].data[coin.coin_symbol][rootState.fiatCurrencies.selectedFiatCurrencyCode]
            if (coin.sell_currency === 'BTC' || coin.sell_currency === 'ETH') coin.acquisition_cost_converted = value
            if (coin.buy_currency === 'BTC' || coin.buy_currency === 'ETH') coin.acquisition_cost_converted = value
            return coin
        })
        .catch(err => { console.log(err) })
},

问题是第一个函数没有等待第二个函数完成。如何调整此代码以解决此问题?

谢谢

【问题讨论】:

  • 尝试将您的示例缩减为一个仍然显示问题的最小独立示例。如果你很幸运,这样做就足以让你自己弄清楚问题所在。如果没有,其他人会更容易看到并帮助您。另请参阅sscce.org,了解有关如何减少和生成良好示例的更多提示。
  • createAcqCostConverted 有时会返回 coin (即if the selected fiatCurrency is the same as the buy_currency - 在这种情况下,任何被推送到承诺的 Promise 都将“丢失”)并且在其他时候返回 undefined ...并且永远不会返回一个 Promise - 最后的 Promise.all 语句不会“神奇地”返回一个 Promise,如果你想返回一个 Promise,你需要 return Promise.all
  • @JaromandaX 谢谢,我很困惑将返回值放在哪里,如果在 .then() 内或在哪里,这消除了一些混乱.. 仍然第一个函数不能作为预计
  • 是的,在 .then 内也是如此 - 如果第二个函数没有返回你期望它返回承诺的承诺,那么第一个函数(即期望来自第二个函数的承诺或值)不会工作

标签: javascript vue.js promise vuejs2 vuex


【解决方案1】:

您正在同时执行所有承诺。 Promise.all 不按顺序执行它们。您传递的数组的顺序无关紧要。它只是在它们都完成时解决,无论顺序如何。

执行发生在您调用函数时,甚至在您将它们推入数组之前。

如果您需要在调用第二个之前等待第一个完成。您需要在第一个 .then 函数中调用第二个。比如……

dispatch('utilities/setLoading', true, { root: true }).then(resultOfSetLoading => {
   return Promise.all(coins.map(coin =>  dispatch('createAcqCostConverted', coin)))
}).then(resultOfCreateQcqCostConverted => {
   // all createAcqCostConverted are complete now
})

所以现在,dispatch('utilities/setLoading') 将首先运行。然后,一旦完成,dispatch('createAcqCostConverted') 将为每个硬币运行一次(同时因为我使用了Promise.all)。

我建议您详细了解Promise.all 的工作原理。假设它按顺序解决它们是很自然的,但事实并非如此。

【讨论】:

  • 感谢您的帮助。我正在调整第一个功能以匹配您的建议以及第二个功能,显然它也需要一些工作,而且似乎我正在以某种方式使其工作。不过我想再测试一下...问题:为什么是 .map 而不是 .forEach?
  • 我切换到地图,因为我将结果传递给Promise.allmap 返回其回调返回的所有值的数组。 forEach 返回undefined。如果我在那里使用了forEach,我会将undefined 传递给Promise.all,而不是一组承诺
【解决方案2】:

在阅读了你们中的一些人的回复后,这就是我设法使其工作的方法(对于注销用户和登录用户,两种不同的方法),不确定这是否是最干净的方法。

第一个函数:

fetchUserPortfolioCoins({ commit, dispatch, state, rootGetters }) {
    const setCoinsPromise = []
    let coinsToConvert = null
    // start loader in template
    dispatch('utilities/setLoading', true, { root: true })
    // if user is logged off, use the coins in the state as dispatch param for createAcqCostConverted
    if (!rootGetters['auth/isAuthenticated']) setCoinsPromise.push(coinsToConvert = state.userPortfolioCoins)
    // otherwise we pass the coins in the DB
    else setCoinsPromise.push(Vue.axios.get('/api/coins/').then(response => { coinsToConvert = response.data }))

    // once the call to the db to fetch the coins has finished
    Promise.all(setCoinsPromise)
        // for each coin retrived, create the converted acq cost
        .then(() => Promise.all(coinsToConvert.map(coin => dispatch('createAcqCostConverted', coin))))
        .then(convertedCoins => {
            // finally, set the portfolio coins and portfolio overview values, and stop loader
            commit('SET_USER_COINS', { coins: convertedCoins, list: 'userPortfolioCoins' })
            commit('SET_USER_PORTFOLIO_OVERVIEW')
            dispatch('utilities/setLoading', false, { root: true })
        }).catch(err => { console.log(err) })
},

createAcqCostConverted 函数:

createAcqCostConverted({ dispatch, rootState }, coin) {
    const promises = []
    // this check is only going to happen for sold coins, we are adding sell_price_converted in case user sold in BTC or ETH
    if (coin.sell_currency === 'BTC' || coin.sell_currency === 'ETH') {
        const URL = `https://min-api.cryptocompare.com/data/pricehistorical?fsym=${coin.coin_symbol}&tsyms=${rootState.fiatCurrencies.selectedFiatCurrencyCode}&ts=${coin.sold_on_ts}`
        promises.push(Vue.axios.get(URL, {
            transformRequest: [(data, headers) => {
                delete headers.common.Authorization
                return data
            }]
        }))
    }
    // if user bought with BTC or ETH we convert the acquisition cost to the currently select fiat currency, using the timestamp
    if (coin.buy_currency === 'BTC' || coin.buy_currency === 'ETH') {
        const URL = `https://min-api.cryptocompare.com/data/pricehistorical?fsym=${coin.coin_symbol}&tsyms=${rootState.fiatCurrencies.selectedFiatCurrencyCode}&ts=${coin.bought_on_ts}`
        promises.push(Vue.axios.get(URL, {
            transformRequest: [(data, headers) => {
                delete headers.common.Authorization
                return data
            }]
        }))
    } else {
        // if the selected fiatCurrency is the same as the buy_currency we skip the conversion
        if (coin.buy_currency === rootState.fiatCurrencies.selectedFiatCurrencyCode) {
            promises.push(coin.acquisition_cost_converted = NaN)
            // otherwise we create the acq cost converted property
        } else promises.push(dispatch('fiatCurrencies/convertToFiatCurrency', coin, { root: true }))
    }
    return Promise.all(promises)
        .then(response => {
            if (coin.sell_currency === 'BTC' || coin.sell_currency === 'ETH') {
                const value = response[0].data[coin.coin_symbol][rootState.fiatCurrencies.selectedFiatCurrencyCode]
                coin.acquisition_cost_converted = value
            }
            if (coin.buy_currency === 'BTC' || coin.buy_currency === 'ETH') {
                const value = response[0].data[coin.coin_symbol][rootState.fiatCurrencies.selectedFiatCurrencyCode]
                coin.acquisition_cost_converted = value
            }
            return coin
        })
        .catch(err => { console.log(err) })
},

在第二个函数中我不需要做太多调整,我只是为 Promise.all 添加了一个“return”,并更正了 if/else 以仅在特定原因中使用响应,因为生成了“value”变量从响应仅在这两种情况下有效,在其他情况下我可以简单地返回“硬币”。

希望这是有道理的,如果需要,在这里可以更好地解释任何事情和/或讨论使代码更好的方法(我觉得不是,但不知道为什么:P)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多