【问题标题】:Chaining promises resulting in undefined链接承诺导致未定义
【发布时间】:2014-05-29 06:34:41
【问题描述】:

我在这个函数中不断收到未定义的错误。它分为 5 个单独的函数,我试图在前一个函数完成后运行每个函数,但使用前一个函数返回的数据。我可以嵌套所有回调,但我宁愿使用 Promise。

var checkedOutReport = function () { 
    var rootSite = window.location.protocol + "//" + window.location.hostname;

    //Create array with each collection site
    function getAllSites (){
        var d = $q.defer();
        var allSites = [];

        $().SPServices({
            operation: "GetAllSubWebCollection",
            completefunc: function (xData,Status){
                var result = $(xData.responseXML);

                result.find('Web').each(function (){
                    var self = $(this);
                    allSites.push({
                        siteTitle: self.attr('Title'),
                        siteUrl: self.attr('Url')   
                    })
                })  
            }
        })
        d.resolve(allSites)
        return d.promise;
    }
    //Get libraries for each siteUrl
    function getSiteLibraries (allSites) {
        var siteCount = allSites.length;
        var listPromise = [];

        //Go through each site and find document libraries 
        for (var i = 0; i < siteCount; i++){
            listPromise[i] = $().SPServices({
                operation: "GetListCollection",
                webURL: allSites[i].siteUrl
            })
        }

        //Promise array contains all lists for each site
        //Get libraries for each site 
        $q.all(listPromise).then(findLibraries(listPromise))
    }
    function findLibraries (promises){
        var d = $q.defer();
        var siteCount = promises.length;
        var allLibraries = [];
        //For each site, find the document libraries
        for (var i = 0; i < siteCount; i++){
            $(promises[i].responseXML).find("List[ServerTemplate='101']").each(function(){
                var self = $(this);
                allLibraries.push({
                    listName = self.attr('Title'),
                    listId = self.attr('ID'),
                    siteUrl = allSites[i].siteUrl
                })
            })
        }
        console.log(allLibraries);
        d.resolve(allLibraries);
        return d.promise;
    }

    //CAML QUERIES
    var cQueryOptions = "<QueryOptions><ViewAttributes Scope='RecursiveAll' IncludeRootFolder='True' /></QueryOptions>",
        cQueryAllCheckedOutDocuments = '<Query><Where><IsNotNull><FieldRef Name="CheckoutUser" /></IsNotNull></Where></Query>',
        cQueryModifiedBySystemAccount = '<Query><Where><Contains><FieldRef Name="Editor" /><Value Type="User">system</Value></Contains></Where></Query>',
        cViewFields = "<ViewFields Properties='True' />";

    //For each library in site collection, find documents which meet criteria 
    function searchLibraries (library) {
        var libraryCount = library.length;
        var itemPromise = [];

        for (var i = 0; i < libraryCount; i++){
            itemPromise[i] = $().SPServices({
                operation: "GetListItems",
                webURL: library[i].siteUrl,
                listName: library[i].listID,
                CAMLViewFields: cViewFields,
                CAMLQuery: cQueryAllCheckedOutDocuments
            })
        }

        $q.all(itemPromise).then(parseSearchResult(itemPromise))
    }
    //Clean up result
    function parseSearchResult(result){
        var d = $q.defer()
        var resultCount = result.length; 
        var returnedFiles = [];
        for(var i = 0; i < resultCount; i++){
            $(result[i].responseXML).SPFilterNode("z:row").each(function (){
                var self = $(this);

                var fileName = self.attr('ows_LinkFilename');
                var fileUrl = self.attr('ows_FileDirRef').split("#");
                var checkedTo = self.attr('ows_LinkCheckedOutTitle');
                var modified = self.attr('ows_Modified');

                returnedFiles.push({
                    fileName: fileName,
                    fileUrl: fileUrl[1],
                    checkedTo: checkedTo,
                    modified: modified
                });
            })
        }
        d.resolve(returnedFiles);
        return d.promise;
    }

    getAllSites()
        .then(
            getSiteLibraries(allSites)
            )
        .then(
            searchLibraries(allLibraries)
            );
}

【问题讨论】:

  • $().SPServices() 是否返回承诺? getAllSites 会让我们相信它不会,而getSiteLibrariessearchLibraries 会让我们相信它会。 ——
  • 分号纪律!
  • 是的 SPServices 总是返回一个承诺,但我并不总是使用它。例如,getAllSites 只进行一次网络调用,所以我真的不需要它。但是,searchLibraries 正在通过一个 for 循环进行多个操作,因此我在 $q.all() 中使用了 promise
  • 蝙蝠侠,在这种情况下,$().SPServices 似乎有点奇怪,因为它既接受了completefunc 回调又返回了一个承诺。您通常会期望其中之一。当然,这两种机制可能揭示不同的数据,甚至会更奇怪,但是如果没有 SPServices 的具体知识,谁也无法分辨。文档是否提供任何见解。此外,进行单个调用本身并不是不使用返回的承诺的原因。
  • 它们都返回相同的信息,只是如果我使用 completefunc 回调,在回调中发生的任何事情完成之前,我无法进行异步调用,这会大大降低应用程序的速度。使用 Promise,我可以让所有 Web 调用异步地将 Promise 存储在一个数组中,然后从那里使用它们。不过,我并不是真正的 Promise 专家,但仍在试图弄清楚为什么我在让事情正常工作时遇到问题。

标签: javascript jquery angularjs promise jquery-deferred


【解决方案1】:

这里有很多问题。我会尽我所能描述尽可能多的。可能还有几个...

d.resolve(allSites) 需要在completefunc 回调中,因为在填充allSites 之前,您不想解决承诺。

getSiteLibraries()searchLibraries() 需要返回他们的承诺。

findLibraries()parseSearchResult() 没有任何异步代码,因此无需返回承诺,只需返回结果即可。

$q.all(listPromise).then(findLibraries(listPromise)) 实际上会立即执行findLibraries(),只需使用$q.all(listPromise).then(findLibraries)$q.all(itemPromise).then(parseSearchResult(itemPromise)) 也是如此。

同样适用...

getAllSites()
    .then(
        getSiteLibraries(allSites)
        )
    .then(
        searchLibraries(allLibraries)
        );

改为:

getAllSites()
    .then(
        getSiteLibraries
        )
    .then(
        searchLibraries
        );

应用了上述更改...

var checkedOutReport = function () { 
    var rootSite = window.location.protocol + "//" + window.location.hostname;

    //Create array with each collection site
    function getAllSites (){
        var d = $q.defer();
        var allSites = [];

        $().SPServices({
            operation: "GetAllSubWebCollection",
            completefunc: function (xData,Status){
                var result = $(xData.responseXML);

                result.find('Web').each(function (){
                    var self = $(this);
                    allSites.push({
                        siteTitle: self.attr('Title'),
                        siteUrl: self.attr('Url')  
                    })
                })  
                d.resolve(allSites); // resolve only when complete
            }
        });
        return d.promise;
    }
    //Get libraries for each siteUrl
    function getSiteLibraries (allSites) {
        var siteCount = allSites.length;
        var listPromise = [];

        //Go through each site and find document libraries 
        for (var i = 0; i < siteCount; i++){
            listPromise[i] = $().SPServices({
                operation: "GetListCollection",
                webURL: allSites[i].siteUrl
            })
        }

        //Promise array contains all lists for each site
        //Get libraries for each site 
        return $q.all(listPromise).then(findLibraries); // need to return the promise
    }
    function findLibraries (promises){
        var siteCount = promises.length;
        var allLibraries = [];
        //For each site, find the document libraries
        for (var i = 0; i < siteCount; i++){
            $(promises[i].responseXML).find("List[ServerTemplate='101']").each(function(){
                var self = $(this);
                allLibraries.push({
                    listName: self.attr('Title'),
                    listId: self.attr('ID'),
                    siteUrl: allSites[i].siteUrl
                })
            })
        }
        console.log(allLibraries);
        return allLibraries; // don't need a promise here
    }

    //CAML QUERIES
    var cQueryOptions = "<QueryOptions><ViewAttributes Scope='RecursiveAll' IncludeRootFolder='True' /></QueryOptions>",
        cQueryAllCheckedOutDocuments = '<Query><Where><IsNotNull><FieldRef Name="CheckoutUser" /></IsNotNull></Where></Query>',
        cQueryModifiedBySystemAccount = '<Query><Where><Contains><FieldRef Name="Editor" /><Value Type="User">system</Value></Contains></Where></Query>',
        cViewFields = "<ViewFields Properties='True' />";

    //For each library in site collection, find documents which meet criteria 
    function searchLibraries (library) {
        var libraryCount = library.length;
        var itemPromise = [];

        for (var i = 0; i < libraryCount; i++){
            itemPromise[i] = $().SPServices({
                operation: "GetListItems",
                webURL: library[i].siteUrl,
                listName: library[i].listID,
                CAMLViewFields: cViewFields,
                CAMLQuery: cQueryAllCheckedOutDocuments
            })
        }

        return $q.all(itemPromise).then(parseSearchResult); // need to return promise
    }
    //Clean up result
    function parseSearchResult(result){
        var resultCount = result.length; 
        var returnedFiles = [];
        for(var i = 0; i < resultCount; i++){
            $(result[i].responseXML).SPFilterNode("z:row").each(function (){
                var self = $(this);

                var fileName = self.attr('ows_LinkFilename');
                var fileUrl = self.attr('ows_FileDirRef').split("#");
                var checkedTo = self.attr('ows_LinkCheckedOutTitle');
                var modified = self.attr('ows_Modified');

                returnedFiles.push({
                    fileName: fileName,
                    fileUrl: fileUrl[1],
                    checkedTo: checkedTo,
                    modified: modified
                });
            })
        }
        return returnedFiles; // don't need a promise here
    }

    getAllSites()
        .then(
            getSiteLibraries
            )
        .then(
            searchLibraries
            );
}

【讨论】:

  • 再看看allLibraries.push() 声明。
  • 请从该代码中删除那个丑陋的deferred anti pattern :)
  • @Roamer-1888 谢谢,我已经进去纠正了。
  • @anthony 如果我执行 $q.all(listPromise).then(findLibraries),我该如何在 findLibraries() 中使用结果“listPromise”,因为该函数需要一个参数。我很难弄清楚以前函数的结果如何发送到下一个函数。例如, findLibraries() 的结果如何传递给 searchLibraries() ?
  • @Batman 它作为函数的第一个参数作为结果数组传递。阅读文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-19
  • 2017-01-19
相关资源
最近更新 更多