redux thunk - how dispatch data in nested array after promise finish

こ雲淡風輕ζ 提交于 2019-12-25 16:45:04

问题


I want to turn all 'default text' in newArray to 'new text'. Then dispatch the array with 'new text'. The problem is dispatch function is dispatching the 'default text'. Look like it does not wait for promise. What's wrong with my promise setup in the code below?

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

回答1:


Your input data structure is like this:

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

Your code transforms it into this:

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

So at the outer level there is no way of knowing when the inner promises have finished. We need extra layers of promises to move that information up the object graph. Essentially, we need:

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

So the top level promise can resolve when all the inner ones do. The code that does that would look very similar:

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

You can use arrow functions if you think that improves clarity (I don't):

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

General remark: I have removed the callback function from your code. It contradicts the philosophy behind promises to call code continuation callbacks from within them.

Instead of doing this (essentially your code):

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

do this:

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

Note how the second variant no longer has any dependency on its caller. It simply carries out its task and returns the result. The caller can decide what to do with it:

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



回答2:


Nesting, asynchronism and the requirement to clone (or mimic) make this slightly tricky :

You could build the required array as an outer variable :

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

Alternatively, it's possible to dispense with the outer variable and allow the promises to convey the data :

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

Barring mistakes on my part, both versions should work.

Injection of defaults on error could be more comprehensive, if desired.



来源:https://stackoverflow.com/questions/40791724/redux-thunk-how-dispatch-data-in-nested-array-after-promise-finish

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!