Написание/настройка объекта из обратного вызова

Я работаю над проектом реакции на метеор и хочу использовать данные загрузки из локального хранилища, которые происходят асинхронно. К сожалению, я не смог получить данные из обратного вызова даже с привязками. Я пробовал несколько способов, но не смог заставить ни один из них работать, наверняка я что-то упускаю.

Я вырезал столько, сколько мог, но кое-что пришлось оставить для контекста.

Насколько я понимаю, это может быть связано только с объектом дорожки, поскольку установка этих простых целых чисел, логические значения работают нормально.

render() {
    const { audioCollection } = this.props; // database collection, async hence the following if check
    if (audioCollection) {
        this.tracks = audioCollection.audio(); // setting tracks for later use
        // make sure tracks are loaded and only run once, as we do this in the react renderer
        if (this.tracks && !this.state.tracksLoaded) { 
            var trackLoadedCount = 0;
            this.tracks.forEach((track, i) => { // I tried to use forEach and map here
                // now we try to load the data from local storage and if it fails fall back to the remote server
                LocalForage.getItem(track.file_id).then(function(err, file) {
                    if (!err && file) {
                        console.log(track.file_id + ' from cache')
                        var blob = new Blob([file]);
                        fileURI = window.URL.createObjectURL(blob);
                    } else {
                        console.log(track.file_id + ' from database')
                        fileURI = audioCollection.audioLink(track.file_id);
                    }
                    track.fileURI = fileURI; // assigning the retrieved data uri to the track object, also tried to set it on the original parent object not the extracted one from the forEach/map
                    console.log(fileURI + ' ' + track.fileURI) // yes, both are set
                    trackLoadedCount++; // increasing the tracks loaded count, to calculate if all have been loaded and to test if it can write outside of the callback, which it does
                    // if all files have been processed, set state loaded, this works too.
                    if (trackLoadedCount == this.tracks.length) {
                        this.setState({
                            tracksLoaded: true,
                        })
                    }
                }.bind(track, this))
            });
        }
    }
    // once all has been processed we try to retrieve the fileURI, but it isn't set :(
    if (audioCollection && this.tracks && this.state.tracksLoaded) {
        console.log('all loaded ' + this.tracks.length)
        this.tracks.map(track => {
            console.log('track: ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
        })
    }

    // we only log
    return (
        <Content {...this.props}>
            <div>just console output</div>
        </Content>
    );
}

Я пробовал больше подходов:

  • Запись дорожек в виде массива для состояния типа tracksLoaded (не работает)
  • Определение новой переменной перед асинхронным вызовом и установка ее значений из обратного вызова, например trackLoadedCount (с привязкой и без нее) (не работает)

Почему это не работает, хотя работает для tracksLoaded и trackLoadedCount?

Обновление относительно ответа Фирис Нгуен

render() {
    const { audioCollection } = this.props;
    if (audioCollection) {
        this.tracks = audioCollection.audio();
        if (this.tracks && !this.state.tracksLoaded) {
            var trackLoadedCount = 0;
            this.tracks.forEach((track, i, trackArray) => {
                LocalForage.getItem(track.file_id).then(function(err, file) {
                    if (!err && file) {
                        console.log(track.file_id + ' from cache')
                        var blob = new Blob([file]);
                        fileURI = window.URL.createObjectURL(blob);
                    } else {
                        console.log(track.file_id + ' from database')
                        fileURI = audioCollection.audioLink(track.file_id);
                    }
                    track.fileURI = fileURI;
                    console.log('1. ' + track.file_id + ' ' + track.fileURI);
                    trackArray[i] = track;
                    console.log('2. ' + track.file_id + ' ' + trackArray[i].fileURI);
                    trackLoadedCount++;
                    if (trackLoadedCount == this.tracks.length) {
                        this.setState({
                            tracksLoaded: true,
                        })
                    }
                }.bind(track, this))
            });
        }
    }
    if (audioCollection && this.tracks && this.state.tracksLoaded) {
        console.log('all loaded ' + this.tracks.length)
        this.tracks.map(track => {
            console.log('3. ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
        })
    }

    return (
        <Content {...this.props}>
            <div>just console output</div>
        </Content>
    );
}

возвращается

MXqniBNnq4zCfZz5Q from database
1. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
2. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
keBWP6xb9PyEJhEzo from database
1. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
2. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
K2J2W9W26DDBNoCcg from database
1. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
2. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
all loaded 3
3. MXqniBNnq4zCfZz5Q undefined
3. keBWP6xb9PyEJhEzo undefined
3. K2J2W9W26DDBNoCcg undefined

поэтому проблема сохраняется.


person user2693017    schedule 20.07.2016    source источник


Ответы (1)


forEach выдает копию элемента. track это просто копия, а не оригинал. Он не указывает на элемент в вашем массиве. Вы можете попробовать это:

this.tracks.forEach(track, i, trackArray) => {
    // change `track` value
    ...
    trackArray[i] = track;
});

Или попробуйте метод map

person An Nguyen    schedule 20.07.2016
comment
НЕТ НИ РАЗУ, что forEach делает именно это o.0 В этом нет ни хрена смысла! Я не говорю, что это неправда, но я в шоке. Есть ли у вас какие-либо документальные ссылки на это утверждение? - person Dellirium; 20.07.2016
comment
Я работал с C++ до июня этого года. Может быть, то, как я объясняю, скорее относится к указателю C. Насколько я знаю, в Javascript нет таких вещей, как передача по ссылке, это зависит от контекста вызова. В случае forEach это зависит от обратного вызова, запускающего forEach, а не от самого массива. Пока это то, что я понимаю о Javascript до сих пор. Решение, которое я предлагаю, основано на моем опыте, поправьте меня, если я ошибаюсь. - person An Nguyen; 20.07.2016
comment
К сожалению, раньше я использовал карту, которая тоже не работала, и я также пытался назначить ее родительскому объекту this.tracks[i].fileURI = fileURI;, что тоже не сработало :( - person user2693017; 20.07.2016
comment
Если вы назначаете его внутри блока forEach, я не уверен, что this указывает на ваш компонент, который содержит реквизит tracks. Пожалуйста, используйте массив, как в моем примере. - person An Nguyen; 20.07.2016
comment
@FiriceNguyen в соответствии с этим developer.mozilla.org /en-US/docs/Web/JavaScript/Reference/ функция обратного вызова получает три аргумента, поэтому внутри нее вы можете использовать переданный 1-й аргумент для ссылки на исходный элемент массива или по индексу (2-й аргумент). Теперь, если это поможет здорово, если нет. я не знаю - person Dellirium; 21.07.2016
comment
Согласно ссылке: если параметр thisArg предоставляется для forEach(), он будет передан обратному вызову при вызове для использования в качестве его значения this. А внутри forEach this не указывает на сам массив. Чтобы element, который мы модифицируем внутри, не ссылался на исходный. Я нашел еще один ответ с изображением, чтобы продемонстрировать случай forEach: " title="контекст вызова этого вызова функции foreach"> stackoverflow.com/questions/19707969/ - person An Nguyen; 21.07.2016
comment
@FiriceNguyen Я проверил это именно так, как вы написали, к сожалению, это ничего не изменило, см. обновленный вопрос. - person user2693017; 22.07.2016
comment
Вы пропустили третий параметр в своем forEach: this.tracks.forEach((track, i) => {. Должно быть this.tracks.forEach(track, i, trackArray) => {. Третий параметр trackArray — это единственная точка вашего массива, чтобы позже вы могли назначить trackArray[i] = track; - person An Nguyen; 22.07.2016
comment
ааа извините, это была оплошность при копировании. Я позабочусь о том, чтобы создать минималистичный проект, который можно запустить одной командой, чтобы он не просто гадал. Теперь я должен был сделать это с самого начала. - person user2693017; 22.07.2016