承诺后执行次要行动,而不管结果如何?

问题描述 投票:0回答:1

[我找到了上一个线程(How to perform same action regardless of promise fulfilment?),但是它已有5年历史了,引用winjs有点麻烦。

我想做的是加载数据元素列表。我有列表的本地副本和元素的本地副本-但它们在服务器端可能已更改。

该过程应像这样:将数据库中的LIST加载到本地存储中(与本地比较)->然后从数据库中加载LIST中列出的(多个)DATA ELEMENTS。

因此,如果“ loadList”异步功能成功...我想运行“ loadElements”异步功能。如果loadList函数拒绝...我仍然想运行“ loadElements”函数(将触发多个获取请求-每个元素一个)。

“”使用'finally'“,我听到你说...,但是我想将” loadList“解析/拒绝和” loadElements“解析/拒绝函数的结果传递给调用函数。据我所知,“最终”不会接收或传递属性。

我想要将结果传递给调用函数的原因是,查看拒绝原因是否可以接受,并且我可以信任本地副本是否为权威副本(例如,如果数据库不包含LIST, ,我可以相信本地列表是权威版本)...因此,我需要一种方法来分析调用函数中的“失败”。

这是我所拥有的:

export function syncLinkTablesAndElementsWithDB(username) { 
return (dispatch, getState) => {
        return new Promise((resolve, reject) => {
            dispatch(loadLinkTableAndElementsFromDB(STATIONS_LINK_TABLE_TYPE, username))
                .then((msg) => {
                    console.log("loadLinkTableAndElementsFromDB RESOLVED: ", msg);
                    resolve(msg)

                })
                .then(() => {
                    dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
                    dispatch(pushAllUserStationsToDB(username))
                 })
                .catch((allPromReasons) => {
                    console.log("loadLinkTableAndElementsFromDB REJECTED: ", allPromReasons);
                    allReasonsAcceptable = true;
                    allPromReasons.forEach(reason => {
                        if (!isAcceptableLoadFailureReasonToOverwrite(reason)) {
                            allReasonsAcceptable = false;
                        }
                    });
                    if (allReasonsAcceptable) {
                        //TODO:   DO push of local to DB
                        // eventually return results of the push to DB...

                    } else {
                        reject(allPromReasons)
                    }
                })
        });
    }
}


export function loadLinkTableAndElementsFromDB(tableType, username) {
    return (dispatch, getState) => {
        return new Promise((resolve, reject) => {
            dispatch(loadLinkTableFromDB(tableType, username))
                .then(successMsg => {
                    resolve(Promise.all([successMsg, dispatch(loadAllUsersStationsFromDB(username)).catch(err=>err)]))
                })
                .catch(err => {
                    reject(Promise.all([err, dispatch(loadAllUsersStationsFromDB(username)).catch(err=>err)]))
                })
        });
    }
}

export function loadAllUsersStationsFromDB(username) {
    return (dispatch, getState) => {
        return new Promise((resolve, reject) => {
            let linkTable = getStationsLinkTable(username); // get the local link table
            if (linkTable && Array.isArray(linkTable.stations)) { // if there is a local station list
                let loadPromises = linkTable.stations.map(stationID => dispatch(loadStationFromDB(stationID)).catch((err) => err));
                Promise.all(loadPromises)
                    .then((allReasons) => {
                        let allSuccesses = true;
                        allReasons.forEach(reason => {
                            if (!reason.startsWith(SUCCESS_RESPONSE)) {
                                allSuccesses = false;
                            }
                        });
                        if (allSuccesses) {
                            resolve(SUCCESS_RESPONSE + ": " + username);
                        } else {
                            reject(allReasons);
                        }
                    })
            } else {
                return reject(NO_LINK_TABLE_AVAILABLE + ": " + username);
            }
        });
    };
}

loadStationFromDB和loadLinkTableFromDB可以完成您所期望的...尝试从数据库中加载这些内容。如果您认为值得的话,我可以提供他们的代码。

-----------编辑-----------为了阐明我要完成的工作:

我正在尝试将本地存储与数据库同步。我想通过从数据库中提取数据,比较时间/日期戳来做到这一点。这将使本地存储版本成为所有数据的权威副本。从数据库加载后,我想将本地存储版本推送到数据库。

我需要注意这样一个事实,即数据库通常根本根本没有数据,因此可能会“拒绝”请求,即使在同步的情况下,拒绝也是可以接受的,并且应该不能停止同步过程。

根据下面的建议,我已经修改了代码:

export function loadLinkTableAndElementsFromDB(tableType, username) {
    console.log("loadLinkTableAndElementsFromDB(", tableType, username, ")");
    return (dispatch, getState) => {
        return new Promise((resolve, reject) => {
            dispatch(loadLinkTableFromDB(tableType, username))
                .then(successMsg => {
                    console.log("loadLinkTableFromDB RESOLVED: ", successMsg)
                    resolve(Promise.all([successMsg, dispatch(loadAllUsersStationsFromDB(username)).catch(err => err)]))
                })
                .catch(err => {
                    console.log("loadLinkTableFromDB REJECTED: ", err)
                    reject(Promise.all([err, dispatch(loadAllUsersStationsFromDB(username)).catch(err => err)]))
                })
        });
    }
}

export function syncLinkTablesAndElementsWithDB(username) {
    console.log("syncLinkTablesAndElementsWithDB(", username, ")");
    return (dispatch, getState) => {
        dispatch(loadLinkTableFromDB(STATIONS_LINK_TABLE_TYPE, username))
            .then((successLoadLinkTableMsg) => {
                console.log('Successfully loaded link table: ', successLoadLinkTableMsg)
                return dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
            })
            .catch((rejectLoadLinkTableReason) => {
                console.log("Failed to load link table from DB: " + rejectLoadLinkTableReason);
                if (allReasonsAcceptableForOverwrite(rejectLoadLinkTableReason)) {  // some rejection reasons are accectable... so if failed reason is okay.... 
                    console.log("Failure to load link table reasons were acceptable... pushing local link table anyway");
                    return dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
                } else {
                    console.log("Throwing: ", rejectLoadLinkTableReason);
                    throw rejectLoadLinkTableReason;
                }
            })  
            .then((successPushLinkTaleMsg) => { 
                console.log("Successfully pushed link table: " + successPushLinkTaleMsg);
                return dispatch(loadAllUsersStationsFromDB(username)); // I want this to occur regardless of if the link table stuff succeeds or fails...  but it must occur AFTER the loadLinkTableFromDB at least tries...
            })
            .catch((rejectPushLinkTableReason) => {
                console.log("Failed to push link table: " + rejectPushLinkTableReason);
                return dispatch(loadAllUsersStationsFromDB(username)); // I want this to occur regardless of if the link table stuff succeeds or fails...  but it must occur AFTER the loadLinkTableFromDB at least tries... 
            })
            .then((successLoadAllUserStationsMsg) => {
                console.log("Successfully loaded all user stations: " + successLoadAllUserStationsMsg);
                return dispatch(pushAllUserStationsToDB(username))
            })
            .catch((rejectLoadAllUserStationsReason) => {
                console.log("Failed to push all users stations: " + rejectLoadAllUserStationsReason);
                if (allReasonsAcceptableForOverwrite(rejectLoadAllUserStationsReason)) {  // some rejection reasons are accectable... so if failed reason is okay.... 
                    console.log("Load users stations reasons are acceptable...");
                    return dispatch(pushAllUserStationsToDB(username))
                } else {
                    console.log("throwing: ", rejectLoadAllUserStationsReason);
                    throw rejectLoadAllUserStationsReason;
                }
            })
            .then((successPushAllUserStationsMgs) => {
                console.log("Successfully pushed all users stations: " + successPushAllUserStationsMgs);
                return Promise.resolve();
            })
            .catch((rejectPushAllUserStationsReason) => {
                console.log("Failed to push all users stations: " + rejectPushAllUserStationsReason);
                throw rejectPushAllUserStationsReason;
            })
    };
}


export function syncAllWithDB(username) { 
    return (dispatch, getState) => {

        // other stuff will occur here...

            dispatch(syncLinkTablesAndElementsWithDB(username))  // *** Error here ***
                .then((successMsg) => {
                    console.log("Successful sync for : " + successMsg);
                })
                .catch(allReasons => {
                    console.warn("Link tables and elements sync error: ", allReasons);
                })
        // });
    }
}

[不幸的是,我现在在syncAllWithDB函数的分派中得到'TypeError:dispatch(...)is undefined'。此功能未更改...

javascript redux promise progressive-web-apps thunk
1个回答
1
投票

我并没有完全遵循您要完成的工作(下文中有更多内容),但是要做的第一件事是清理流程,而不要在现有的承诺周围加上多余的new Promise()。从来没有理由这样做:

 function someFunc() {
      return new Promise((resolve, reject) => {
           callSomething.then(result => {
               ...
               doSomethingElse(result).then(result2 => {
                    ...
                    resolve(result2);
               }).catch(err => {
                    ...
                    reject(err);
               });
           }).catch(err => {
               ...
               reject(err);
           });
      });
 }

这是众所周知的诺言反模式。您不需要在已经作出承诺的函数周围包裹额外的手动创建的承诺。相反,您可以只返回您已经拥有的承诺。这称为“承诺链”。您可以从链内的任何地方拒绝或解决链。

 function someFunc() {
     return callSomething.then(result => {
         ...
         // return promise here, chaining this new async operation 
         // to the previous promise
         return doSomethingElse(result).then(result2 => {
              ...
              return result2;
         }).catch(err => {
              ...
              // after doing some processing on the error, rethrow
              // to keep the promise chain rejected
              throw err;
         });
    }).catch(err => {
         ...
         reject err;
    });
 }

或者,您甚至可以像这样拉平承诺链:

 function someFunc() {
     return callSomething.then(result => {
         ...
         return doSomethingElse(result);
     }).then(result2 => {
         ...
         return result2;
    }).catch(err => {
         ...
         throw err;
    });
 }

作为示例,您可以像这样简化syncLinkTablesAndElementsWithDB()

export function syncLinkTablesAndElementsWithDB(username) { 
    return (dispatch, getState) => {
        return dispatch(loadLinkTableAndElementsFromDB(STATIONS_LINK_TABLE_TYPE, username)).then((msg) => {
            console.log("loadLinkTableAndElementsFromDB RESOLVED: ", msg);
            dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
            dispatch(pushAllUserStationsToDB(username))
            // have msg be the resolved value of the promise chain
            return(msg);
        }).catch((allPromReasons) => {
            console.log("loadLinkTableAndElementsFromDB REJECTED: ", allPromReasons);
            let allReasonsAcceptable = allPromReasons.every(reason => {
                return isAcceptableLoadFailureReasonToOverwrite(reason);
            });
            if (allReasonsAcceptable) {
                //TODO:   DO push of local to DB
                // eventually return results of the push to DB...
            } else {
                // have promise stay rejected
                throw allPromReasons;
            }
        });
    }
}

至于您的其余问题,您正在问这个:

因此,如果“ loadList”异步功能成功...我想运行“ loadElements”异步功能。如果loadList函数拒绝...我仍然想运行“ loadElements”函数(将触发多个获取请求-每个元素一个)。

但是,您的代码中没有名为loadList()loadElements()的函数,因此您在那里失去了我,因此我不确定如何提出具体的代码建议。

在promise链中的.then()处理程序内,您可以做三件事:

  1. 返回值。该值成为承诺链的已解决值。
  2. Return a promise。那个诺言附加在诺言链上,当您返回该诺言时,整个诺言链(呼叫者会注意的最上面的诺言)最终都会决定/拒绝。 (或也链接到它的任何内容都可以解析/拒绝)。
  3. 引发异常。将自动监视所有.then()处理程序是否存在异常,如果引发任何异常,则将设置异常值作为拒绝原因,自动拒绝promise链。

因此,这为您提供了最大的灵活性,可以用一个值或一个错误来完成promise链,或将其链接到另一个promise(更多异步操作)。

© www.soinside.com 2019 - 2024. All rights reserved.