【问题标题】:Controlling flow with nested promises in loop在循环中使用嵌套的 Promise 控制流程
【发布时间】:2017-09-05 12:25:52
【问题描述】:

我在使用 Promise 控制我的方法流程时遇到了麻烦:

//FIND CHECKED OUT FILES
getCheckedOutFiles = function () {
    console.log('Get checked out files');
    var d = $q.defer();

    // Store final results and pass to then callback
    var checkedOutFiles = window.x = [];

    // Promise, returns collection with all sites 
    SiteService.getAllSites()
        .then(sites => {
            // For each site get lists then get items matching filter
            sites.forEach(function (site) {
                var web = new $pnp.Web(site.url);

                return web.lists
                    .filter("BaseTemplate eq 101")
                    .get() // Returns promise with all lists matching filter 
                    .then(lists => {
                        // Go through each list and find matching item 
                        lists.forEach(function (list) {
                            web.lists.getByTitle(list.Title).items
                                .filter("CheckoutUser ne null")
                                .expand("File, File/Author, File/ModifiedBy, File/CheckedOutByUser")
                                .get() // Returns promise with all items matching filter 
                                .then(files => {
                                    // Loop through each item, get properties, add to collection 
                                    files.forEach(function (f) {
                                        var fileObject = {
                                            fileName: f.File.Name,
                                            fileUrl: f.File.ServerRelativeUrl,
                                            absoluteUrl: f.File.ServerRelativeUrl,
                                            checkedTo: f.File.CheckedOutByUser.Title,
                                            modified: f.Modified,
                                            modifiedBy: f.File.ModifiedBy.Title,
                                            createdBy: f.File.Author.Title,
                                            created: f.Created,
                                            version: f.File.UIVersionLabel
                                        };
                                        // Add file to collection 
                                        checkedOutFiles.push(fileObject);
                                    }, this);
                                })
                                .catch(e => {
                                    console.log(e);
                                });
                        });
                        // "Ideally" - When all files retrieved return collection of results 
                        d.resolve(checkedOutFiles);
                        return null;
                    })
                    .catch(e => {
                        console.log(e);
                    });
            }, this);
            return null;
        });
    return d.promise;
};

// Returns promise with all checkedout file 
getCheckedOutFiles().then(files => {
    console.log("RESULTS", files);
    d.resolve(files);
});

我注意到console.log("RESULTS", files); 将在调用完成之前打印出来。调用完成后,window.x 将包含预期的数据。

【问题讨论】:

    标签: javascript jquery angularjs promise bluebird


    【解决方案1】:

    使用Promise.all() 等待所有创建的promise,然后解析父promise

    像这样(未测试)

    //FIND CHECKED OUT FILES
    getCheckedOutFiles = function () {
        console.log('Get checked out files');
        var d = $q.defer();
    
        // Store final results and pass to then callback
        var checkedOutFiles = window.x = [];
    
        // Promise, returns collection with all sites 
        SiteService.getAllSites()
            .then(sites => {
                // For each site get lists then get items matching filter
                sites.forEach(function (site) {
                    var web = new $pnp.Web(site.url);
    
                    return web.lists
                        .filter("BaseTemplate eq 101")
                        .get() // Returns promise with all lists matching filter 
                        .then(lists => {
                            let promises = []
                            // Go through each list and find matching item 
                            lists.forEach(function (list) {
    
                                let prom = web.lists.getByTitle(list.Title).items
                                    .filter("CheckoutUser ne null")
                                    .expand("File, File/Author, File/ModifiedBy, File/CheckedOutByUser")
                                    .get() // Returns promise with all items matching filter 
                                    .then(files => {
                                        // Loop through each item, get properties, add to collection 
                                        files.forEach(function (f) {
                                            var fileObject = {
                                                fileName: f.File.Name,
                                                fileUrl: f.File.ServerRelativeUrl,
                                                absoluteUrl: f.File.ServerRelativeUrl,
                                                checkedTo: f.File.CheckedOutByUser.Title,
                                                modified: f.Modified,
                                                modifiedBy: f.File.ModifiedBy.Title,
                                                createdBy: f.File.Author.Title,
                                                created: f.Created,
                                                version: f.File.UIVersionLabel
                                            };
                                            // Add file to collection 
                                            checkedOutFiles.push(fileObject);
                                        }, this);
                                    })
                                    .catch(e => {
                                        console.log(e);
                                    });
                                promises.push(prom)
                            });
                            // "Ideally" - When all files retrieved return collection of results 
                            Promise.all(promises).then(function(){
                                d.resolve(checkedOutFiles);
                            })
                            return null;
                        })
                        .catch(e => {
                            console.log(e);
                        });
                }, this);
                return null;
            });
        return d.promise;
    };
    
    // Returns promise with all checkedout file 
    getCheckedOutFiles().then(files => {
        console.log("RESULTS", files);
        d.resolve(files);
    });
    

    【讨论】:

    • 解决此问题的方法比您显示的要多。 sites.forEach() 中的 return web.lists.filter().get().then() 也不在等待这个承诺。
    • 另外,这是一个承诺 anti-pattern 与 OP 的 d.resolve(),无需将所有这些包装在另一个承诺中。
    【解决方案2】:

    这些是需要解决的问题:

    1. 您有两个单独的循环,您需要在其中管理 Promise。您需要将这些承诺收集到一个数组中,然后在它们上使用Promise.all() 以了解循环中的所有承诺何时完成。

    2. 您通过创建 d 承诺然后手动解决它来使用反模式。相反,您应该返回 SiteService.getAllSites().then() 承诺,然后在其中返回承诺以链接,以便链接所有内容(包括正确的错误处理)。

    3. 您的 .catch() 处理程序只是记录是“吃”错误。如果你 .catch() 并且不重新抛出,它会将这个承诺变成一个已解决的承诺,从而“吃掉”错误而不是传播它。

    以下是解决这些问题的方法:

    //FIND CHECKED OUT FILES
    function getCheckedOutFiles() {
        console.log('Get checked out files');
    
        // Store final results and pass to then callback
        var checkedOutFiles = window.x = [];
    
        // Promise, returns collection with all sites 
        return SiteService.getAllSites().then(sites => {
                // For each site get lists then get items matching filter
                var promises = [];
                sites.forEach(function (site) {
                    var web = new $pnp.Web(site.url);
    
                    promises.push(web.lists
                        .filter("BaseTemplate eq 101")
                        .get() // Returns promise with all lists matching filter 
                        .then(lists => {
                            // Go through each list and find matching item 
                            var promises2 = [];
                            lists.forEach(function (list) {
                                promises2.push(web.lists.getByTitle(list.Title).items
                                    .filter("CheckoutUser ne null")
                                    .expand("File, File/Author, File/ModifiedBy, File/CheckedOutByUser")
                                    .get() // Returns promise with all items matching filter 
                                    .then(files => {
                                        // Loop through each item, get properties, add to collection 
                                        files.forEach(function (f) {
                                            var fileObject = {
                                                fileName: f.File.Name,
                                                fileUrl: f.File.ServerRelativeUrl,
                                                absoluteUrl: f.File.ServerRelativeUrl,
                                                checkedTo: f.File.CheckedOutByUser.Title,
                                                modified: f.Modified,
                                                modifiedBy: f.File.ModifiedBy.Title,
                                                createdBy: f.File.Author.Title,
                                                created: f.Created,
                                                version: f.File.UIVersionLabel
                                            };
                                            // Add file to collection 
                                            checkedOutFiles.push(fileObject);
                                        }, this);
                                    })
                                    .catch(e => {
                                        console.log(e);
                                        // propagate error
                                        throw e;
                                    }));
                            });
                            return Promise.all(promises2);
                        }).catch(e => {
                            console.log(e);
                            // propagate error
                            throw e;
                        }));
                }, this);
                return Promise.all(promises).then(() => {
                    // make checkedOutFiles by the final resolve value
                    return checkedOutFiles;
                });
            });
    };
    
    // Returns promise with all checkedout file 
    getCheckedOutFiles().then(files => {
        console.log("RESULTS", files);
    }).catch(err => {
        // handle error here
    });
    

    这可以通过在 Bluebird Promise 库中使用 Promise.map() 来简化,它将迭代一个数组,调用一个 Promise 生成函数并等待所有 Promise 完成(.each()Promise.all() 的组合) .

    【讨论】:

      猜你喜欢
      • 2020-08-23
      • 2023-04-04
      • 2014-07-09
      • 2017-12-23
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多