2017-11-29 4 views
0

was mein Code versucht zu tun ist, erstellen Sie ein Array von Objekten, die einige dynamische Eigenschaften haben, diese Eigenschaften sind als Ergebnis einiger Funktionen gefüllt werden. Ich versuche, Versprechungen zu verwenden, andernfalls wird meine Vorlage gerendert, bevor die Funktion beendet ist und die Eigenschaften dieser Objekte null oder undefiniert sind, was zu Fehlern in der Vorlage führt.JS - Versprechen in der falschen Reihenfolge

Dies ist die erste Funktion

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

dass dies nennt man:

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

Das Problem ist, dass die erste Funktion nicht für die zweite wartet abzuschließen. Wie kann ich diesen Code anpassen, um das Problem zu beheben?

Dank

+1

Versuchen Sie Ihr Beispiel eine Reduzierung minimal in sich geschlossen, die immer noch das Problem zeigt. Wenn Sie Glück haben, wird es ausreichen, dass Sie selbst herausfinden, was das Problem ist. Wenn nicht, wird es für andere viel einfacher sein, es zu sehen und Ihnen zu helfen. Siehe auch http://www.sscce.org/ für weitere Tipps, wie Sie ein gutes Beispiel erstellen und reduzieren können. – Hjulle

+0

'createAcqCostConverted' gibt manchmal' Münze' (d. H.'Wenn die ausgewählte fiatCurrency die gleiche ist wie die buy_currency' - in diesem Fall wird jedes an Versprechen abgegebene Versprechen" verloren "und zu anderen Zeiten wird" undefiniert "zurückgegeben ... und gibt niemals eine Promise - diese Promise.all-Anweisung zurück am Ende nicht "magisch" eine Zusage zurück, müssen Sie 'Promise.all' zurückgeben, wenn Sie ein Versprechen zurückgeben wollen –

+0

@JaromandaX danke dafür, ich war verwirrt auf, wo die Rückkehr, wenn innerhalb der. then() oder wo, das löscht etwas Verwirrung .. Noch funktioniert die erste Funktion nicht wie erwartet – Giacomo

Antwort

0

Sie sind alle Versprechen zur gleichen Zeit ausgeführt werden. Promise.all führt sie nicht in der Reihenfolge aus. Die Reihenfolge des Arrays, das Sie übergeben, ist nicht relevant. Es löst einfach auf, wenn alle fertig sind, unabhängig von der Reihenfolge.

Die Ausführung geschieht, wenn Sie die Funktionen aufrufen, bevor Sie sie überhaupt in das Array schieben.

Wenn Sie warten müssen, bis der erste fertig ist, bevor Sie den zweiten anrufen. Sie müssen die zweite innerhalb der ersten .then Funktion aufrufen. Zum Beispiel ...

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

Jetzt wird dispatch('utilities/setLoading') zuerst ausgeführt. Nach der Fertigstellung wird dispatch('createAcqCostConverted') für jede Münze einmal ausgeführt (zur gleichen Zeit, seit ich Promise.all verwendet habe).

Ich empfehle Ihnen, ein wenig mehr darüber zu lesen, wie Promise.all funktioniert. Es ist natürlich anzunehmen, dass sie sie in der richtigen Reihenfolge löst, aber das tut es nicht.

+0

Danke für die Hilfe. Ich passe ein wenig die erste Funktion an, um dem zu entsprechen, was du vorgeschlagen hast, und das zweite auch, anscheinend braucht es auch etwas Arbeit und es scheint, als würde ich irgendwie irgendwie arbeiten. Ich möchte es noch mehr testen ... Frage: warum .map und nicht .forEach? – Giacomo

+0

Ich wechselte zur Karte, weil ich das Ergebnis an 'Promise.all' übergebe. 'map' gibt ein Array aller von seinem Callback zurückgegebenen Werte zurück. 'forEach' gibt' undefined' zurück. Wenn ich 'forEach' dort benutzt hätte, würde ich" undefined "statt" Promise.all "statt einer Reihe von Versprechungen weitergeben –

0

Dies ist, wie ich es geschafft habe (sowohl für abgemeldete Benutzer und angemeldete Benutzer, 2 verschiedene Ansätze), nachdem ich einige von euch Antworten gelesen habe, nicht sicher, ob es der sauberste Ansatz ist.

Erste Funktion:

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 Funktion:

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

In der zweiten Funktion, die ich nicht viel einstellen musste, habe ich nur noch eine "Rückkehr" für die Promise.all und korrigierte das if/else, um die Antwort nur in bestimmten Fällen zu verwenden, weil die aus der Antwort erzeugte Variable "value" nur in diesen 2 Fällen gültig ist, in den anderen Fällen könnte ich einfach die "Münze" zurückgeben.

Hoffen, dass es Sinn macht, hier etwas besser zu erklären, falls erforderlich und/oder Möglichkeiten zu diskutieren diesen Code besser zu machen (ich habe das Gefühl, es ist nicht, nicht sicher, warum aber: P)

Verwandte Themen