Я пытаюсь понять, почему AsyncStorage в приложении React Native отказывается выполнять mergeItem с моими данными. Моя функция выглядит так:
const arrToObj = (array) =>
array.reduce((obj, item) => {
obj[Object.keys(item)[0]] = Object.values(item)[0]
return obj
}, {})
export const addThingToThing = async (title, thing) => {
try {
let subThings = []
let things = {}
await AsyncStorage.getItem(THINGS_STORAGE_KEY)
.then((things) => {
let subThings = []
Object.values(JSON.parse(decks))
.map((thing) => {
if (Object.keys(thing)[0] === title) {
subThings = [...Object.values(thing)[0].subThings, subThing]
}
})
return { decks, subThings }
})
.then(({ decks, subThings }) => {
const obj = {
...arrToObj(JSON.parse(things)),
[title]: {
subThings
}
}
console.info(JSON.stringify(obj))
AsyncStorage.mergeItem(THINGS_STORAGE_KEY,
JSON.stringify(obj))
})
} catch (error) {
console.info(`Error adding thing to thing: ${error.message}`)
}
}
Когда я делаю то, что выполняет это, я получаю:
13:35:52: {"test":{"subThings":[{"one":"a","two":"a"}]},"test2":{"title":"test2","questions":[]}}
13:35:55: [Unhandled promise rejection: Error: Value [{"test":{"title":"test","subThings":[]}},{"test2":{"title":"test2","subThings":[]}}] of type org.json.JSONArray cannot be converted to JSONObject]
Это сбивает с толку, потому что когда данные распечатываются, это объект с {...}, но AsyncStorage показывает массив с [...]. Что-то мне не хватает? Мне это кажется довольно глупым, и я не могу понять, как заставить RN вести себя хорошо.
PS. IMO структура данных грубая, но это то, с чем я работаю. Я не решался на это и не могу это изменить.





Я помню, как работал с AsyncStorage, и он принципиально отличается от localStorage, потому что возвращает обещания.
Ваш код выглядит нормально, что очень сбивает с толку, но я внезапно подозреваю, что проблема может быть связана с отсутствующим ключевым словом await.
пытаться:
.then(async ({ decks, subThings }) => {
const obj = {
...arrToObj(JSON.parse(things)),
[title]: {
subThings
}
}
console.info(JSON.stringify(obj))
// We are adding await here
await AsyncStorage.mergeItem(THINGS_STORAGE_KEY, JSON.stringify(obj))
})
Это может быть классическая асинхронная проблема «не дожидаясь разрешения обещания», прежде чем двигаться дальше. Если да, не удаляйте этот вопрос. Это будет полезно для других, включая, вероятно, меня в будущем.
Вот что, я думаю, происходит:
JavaScript бросает AsyncStorage.mergeItem() в очередь функций
Для этой функции, возвращающей обещание, нет await.
Интерпретатор не дожидается его разрешения и сразу переходит к следующей строке кода.
Следующая строка кода отсутствует, поэтому он возвращается из блока then() на 0,1 мс позже (имейте в виду, что он неявно выполняет return undefined, который внутри асинхронной функции совпадает с resolve(undefined), с той точки зрения, что он пытается разрешить все цепочка до await AsyncStorage.getItem(THINGS_STORAGE_KEY)
obj собирает мусор через 0,1 мс после этого
AsyncStorage.mergeItem() наблюдает ложное значение там, где ожидал obj, но я точно не знаю. Возможно, он не выполняет шаг 5 или 6 и вместо этого обнаруживает, что mergeItem все еще ожидает, когда он пытается разрешить цепочку getItem, в любом случае:
Затем AsyncStorage выдает сбивающее с толку сообщение об ошибке
Ах да. Я обновлю свой ответ, добавив в него async. Функция then() должна быть асинхронной, чтобы использовать внутри нее ожидание.
В итоге я использовал для правильной обработки обещаний:
export const addThingToThing = async (title, thing) => {
try {
AsyncStorage.getItem(THINGS_STORAGE_KEY, (err, things) => {
let subThings = []
Object.values(JSON.parse(decks))
.map((thing) => {
if (Object.keys(thing)[0] === title) {
subThings = [...Object.values(thing)[0].subThings, subThing]
}
})
const obj = {
[title]: {
title,
subThings
}
}
console.info(JSON.stringify(obj))
AsyncStorage.mergeItem(THINGS_STORAGE_KEY,
JSON.stringify(obj))
})
} catch (error) {
console.info(`Error adding thing to thing: ${error.message}`)
}
}
Я видел в сети несколько других подобных примеров, в которых использовался .done(), который выглядел идентично тому, что я сделал, кроме этого звонка. Возможно, это тоже можно было попробовать, но у меня это сработало.
Я пробовал использовать
awaitдля второго обещания, но получал ошибку о том, что это зарезервированное ключевое слово. Ваш ответ побудил меня использовать немного другой синтаксис, который я опубликую ниже. Спасибо!