Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
360 views
in Technique[技术] by (71.8m points)

javascript - 不管结果如何,都应承诺后再采取第二行动?(Run secondary action after promise regardless of outcome?)

I found this previous thread ( How to perform same action regardless of promise fulfilment? ), but it's 5 years old and references winjs is a kludge.

(我找到了这个先前的线程( 无论是否履行承诺,如何执行相同的操作? ),但是它已有5年历史了,引用winjs是一个麻烦。)

What I would like to do is load a list of data elements.

(我想做的是加载数据元素列表。)

I've got local copies of the list and local copies of the elements -- but they may have changed on the server side.

(我有列表的本地副本和元素的本地副本-但它们在服务器端可能已更改。)

That process should work like this: load the LIST from the database into the local storage (Comparing against the local) --> THEN load the (multiple) DATA ELEMENTS from the database that are listed in the LIST.

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

So if the "loadList" async function succeeds... I want to run the "loadElements" async function.

(因此,如果“ loadList”异步功能成功...我想运行“ loadElements”异步功能。)

If the loadList function rejects... I STILL want to run the "loadElements" function (Which fires off multiple fetch requests - one for each element).

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

"Use 'finally'" I hear you say... but I want to pass the results of the "loadList" resolve/reject and "loadElements" resolve/reject functions to the calling function.

(我听到您说“使用'finally'”,但是我想将“ loadList”解析/拒绝和“ loadElements”解析/拒绝函数的结果传递给调用函数。)

'finally' doesn't receive or pass properties as far as I know.

(据我所知,“最终”不会接收或传递属性。)

The reason I want to pass the results to the calling function is to see if the rejection reasons are acceptable reasons and I can trust the local copy as the authoritative copy or not (for example, if the DB doesn't contain the LIST, I can trust that the local list is the authoritative version)... so I need a way to analyze the 'failures' within the calling function.

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

Here is what I have:

(这是我所拥有的:)

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 and loadLinkTableFromDB do what you'd expect... try to load those things from from the DB.

(loadStationFromDB和loadLinkTableFromDB可以实现您所期望的...尝试从数据库中加载这些内容。)

I can include their code if you think it's worthwhile.

(如果您认为值得的话,我可以提供他们的代码。)

----------- EDIT ----------- To clarify what I'm trying to accomplish:

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

I'm trying to sync local storage with a database.

(我正在尝试将本地存储与数据库同步。)

I want to do this by pulling the data from the database, compare the time/datestamps.

(我想通过从数据库中提取数据,比较时间/日期戳来做到这一点。)

This will make the local storage version the authoritative copy of all the data.

(这将使本地存储版本成为所有数据的权威副本。)

After the loads from the DB, I'd like to then push the local storage version up to the DB.

(从数据库加载后,我想将本地存储版本推送到数据库。)

I need to care for the fact that the database will often simply not have the data at all, and thus might 'reject' on a pull... even though, in the instance of a sync, that rejection is acceptable and should not stop the sync process.

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

Per suggestions below, I've modified my code:

(根据以下建议,我已经修改了代码:)

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 => {
  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I don't entirely follow what you're trying to accomplish (more on that below), but the first thing to do here is to clean up the flow and not wrap an extra new Promise() around existing promises.

(我没有完全遵循您要完成的工作(更多内容在下文中),但是这里要做的第一件事是清理流程,而不是在现有的new Promise()周围包装额外的new Promise() 。)

There is never a reason to do this:

(从来没有理由这样做:)

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

That is a well-known promise anti-pattern.

(那是众所周知的诺言反模式。)

You don't need the extra manually created promise wrapped around your function that already makes a promise.

(您不需要在已经作出承诺的函数周围包裹额外的手动创建的承诺。)

Instead, you can just return the promise you already have.

(相反,您可以只返回您已经拥有的承诺。)

This is called "promise chaining".

(这称为“承诺链”。)

From within the chain you can reject or resolve the chain from anywhere.

(在链内,您可以从任何地方拒绝或解决链。)

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

Or, you can even flatten the promise chain like this:

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

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

As an example of that, you can simplify syncLinkTablesAndElementsWithDB() like this:

(例如,您可以像这样简化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;
            }
        });
    }
}

As for the rest of your question, you're asking this:

(至于其余的问题,您正在问:)

So if the "loadList" async function succeeds... I want to run the "loadElements" async function.

(因此,如果“ loadList”异步功能成功...我想运行“ loadElements”异步功能。)

If the loadList function rejects... I STILL want to run the "loadElements" function (Which fires off multiple fetch requests - one for each element).

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

But, there are not functions in your code called loadList() and loadElements() so you lost me there so I'm not sure how to make a specific code suggestion.

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

Inside a .then() handler in a promise chain, you can do three things:

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

  1. Return a value.

    (返回一个值。)

    That value becomes the resolved value of the promise chain.

    (该价值成为承诺链的解决价值。)

  2. Return a promise.

    (兑现承诺。)

    That promise is attached to the promise chain and the whole promise chain (the top-most promise that a caller would be watching) will eventually resolve/reject when this promise you are returning resolves/rejects (or anything that is also chained onto it resolves/rejects).

    (该诺言附加到诺言链,整个诺言链(呼叫者将看到的最上面的诺言)最终将在您返回确定/拒绝的诺言(或也链接到它的任何东西)上解决/拒绝。 /拒绝)。)

  3. Throw an exception.

    (引发异常。)

    All .then() handlers are automatically watched for exceptions and if any exception is throw, then the promise chain is automatically rejected with the exception value set as the reject reason.

    (将自动监视所有.then()处理程序是否存在异常,如果引发任何异常,则将以设置为拒绝原因的异常值自动拒绝promise链。)

So, that gives you the ultimate flexibility to finish the promise chain with a value or an error or to link it to another promise (more asynchronous operations).

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...