JS - 承诺以错误的顺序执行
JS - Promises executing in the wrong order
我的代码试图做的是创建一个具有一些动态属性的对象数组,这些属性将作为某些函数的结果被填充。我正在尝试使用承诺,否则我的模板会在函数完成之前呈现,并且这些对象的属性将为 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) })
},
问题是第一个函数没有等待第二个函数完成。我该如何调整此代码来解决问题?
谢谢
您正在同时执行所有承诺。 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 工作原理的内容。很自然地认为它会按顺序解决它们,但事实并非如此。
在我阅读了你们中的一些人的回复后,这就是我设法使其工作的方式(对于注销用户和登录用户,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" 变量仅在这两种情况下有效,在其他情况下我可以简单地 return "coin".
希望它有意义,如果需要,可以在这里更好地解释任何事情and/or讨论如何使这段代码更好(我觉得它不是,但不确定为什么 :P )
我的代码试图做的是创建一个具有一些动态属性的对象数组,这些属性将作为某些函数的结果被填充。我正在尝试使用承诺,否则我的模板会在函数完成之前呈现,并且这些对象的属性将为 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) })
},
问题是第一个函数没有等待第二个函数完成。我该如何调整此代码来解决问题?
谢谢
您正在同时执行所有承诺。 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 工作原理的内容。很自然地认为它会按顺序解决它们,但事实并非如此。
在我阅读了你们中的一些人的回复后,这就是我设法使其工作的方式(对于注销用户和登录用户,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" 变量仅在这两种情况下有效,在其他情况下我可以简单地 return "coin".
希望它有意义,如果需要,可以在这里更好地解释任何事情and/or讨论如何使这段代码更好(我觉得它不是,但不确定为什么 :P )