redux thunk - как отсылать данные во вложенный массив после завершения обещания

Я хочу превратить весь текст по умолчанию в newArray в "новый текст". Затем отправьте массив с "новым текстом". Проблема в том, что функция отправки отправляет "текст по умолчанию". Похоже, это не ждет обещания. Что не так с настройкой моего обещания в приведенном ниже коде?

return dispatch => {
    let newarray =[ 
        { post:[ {message:'default text'}, {message:'default text'}] }
    ]
    let quests = newarray.map( (i) => {
        return i.post.map( (item) => {
            return axios.get(someLink).then( result =>{
                item.message = 'new text'
                return result
            })
        })
    })

    Promise.all(quests).then( () => {
        dispatch({
            type: constant.GET_SUCCESS,
            payload: newarray
        })
    }).catch( () =>{
        console.log('no result')
    })
}

2 ответа

Ваша структура входных данных выглядит следующим образом:

[
    {
        post: [
            {message:'default text'},
            {message:'default text'}
        ]
    }
]

Ваш код превращает его в это:

[
    [
        Promise<Axios>,
        Promise<Axios>
    ]
]

Итак, на внешнем уровне нет способа узнать, когда внутренние обещания закончились. Нам нужны дополнительные слои обещаний для перемещения этой информации вверх по графу объектов. По сути, нам нужно:

Promise<[
    Promise<[
        Promise<Axios>,
        Promise<Axios>
    ]>
]>

Так что обещание высшего уровня может разрешиться, когда все внутренние сделают. Код, который делает это, будет выглядеть очень похоже:

return function () {
    var newarray = [{ post: [{ message: 'default text' }, { message: 'default text' }] }];

    return Promise.all(newarray.map(function (i) {
        return Promise.all(i.post.map(function (item) {
            return axios.get(someLink).then(function (result) {
                item.message = 'new text';
            });
        }));
    })).then(function () {
        return {
            type: constant.GET_SUCCESS,
            payload: newarray
        };
    }).catch(function (error) {
        return {
            type: constant.GET_ERROR,
            payload: 'no result ' + error
        };
    });
};

Вы можете использовать функции стрелок, если вы думаете, что это улучшает ясность (я не делаю):

return () => {
    var newarray = [{ post: [{ message: 'default text' }, { message: 'default text' }] }];

    return Promise.all(newarray.map( i => Promise.all(
        i.post.map( item => axios.get(someLink).then( result => {
            item.message = 'new text';
        }) )
    ))).then( () => ({
        type: constant.GET_SUCCESS,
        payload: newarray
    })).catch( (error) => ({
        type: constant.GET_ERROR,
        payload: 'no result ' + error
    }));
};

Общее замечание: я удалил функцию обратного вызова из вашего кода. Это противоречит философии, лежащей в основе обещаний вызывать обратные вызовы продолжения кода внутри них.

Вместо этого (по сути, ваш код):

function bla(callback) {
   asyncFunction().then(someProcessing).then(callback);
}

сделай это:

function blaAsync() {
   return asyncFunction().then(someProcessing);
}

Обратите внимание, что второй вариант больше не зависит от вызывающего. Он просто выполняет свою задачу и возвращает результат. Абонент может решить, что с ним делать:

blaAsync().then(function (result) {
   // what "callback" would do
})

Вложенность, асинхронность и требование клонировать (или подражать) делают это немного хитрым:

Вы можете построить требуемый массив как внешнюю переменную:

function getMessagesAndDispatch(array) {
    try {
        let array_ = []; // outer variable, mimicking `array`
        let outerPromises = array.map((a, i) => {
            array_[i] = { 'post': [] }; // mimicking `a`
            let innerPromises = a.post.map((item, j) => {
                array_[i].post[j] = {}; // mimicking `item`
                return axios.get(getOpenGraphOfThisLink + item.message).then(result => {
                    array_[i].post[j].message = result.data;
                }).catch((e) => {
                    array_[i].post[j].message = 'default text';
                });
            });
            return Promise.all(innerPromises);
        });
        return Promise.all(outerPromises).then(() => {
            dispatch({
                'type': constant.GET_SUCCESS,
                'payload': array_
            });
        }).catch((e) => {
            console.log('no result');
            throw e;
        });
    } catch(e) {
        // in case of a synchronous throw.
        return Promise.reject(e);
    }
}

В качестве альтернативы, можно обойтись без внешней переменной и разрешить обещаниям передавать данные:

function getMessagesAndDispatch(array) {
    try {
        let outerPromises = array.map(a => {
            let innerPromises = a.post.map((item) => {
                return axios.get(getOpenGraphOfThisLink + item.message).then(result => {
                    return { 'message': result.data }; // mimicking `item`
                }).catch((error) => {
                    return { 'message': 'default text' }; 
                });
            });
            return Promise.all(innerPromises).then((items_) => {
                return { 'post': items_ }; // mimicking `a`
            });
        });
        return Promise.all(outerPromises).then((array_) => {
            dispatch({
                'type': constant.GET_SUCCESS,
                'payload': array_ // mimicking `array`
            });
        }).catch((e) => {
            console.log('no result');
            throw e;
        });
    } catch(e) {
        // in case of a synchronous throw.
        return Promise.reject(e);
    }
}

За исключением ошибок с моей стороны, обе версии должны работать.

Ввод значений по умолчанию при ошибке может быть более полным, если это необходимо.

Другие вопросы по тегам