将 $.Deferred() 与嵌套的 $.each 语句一起使用

Using $.Deferred() with nested $.each statements

我正在使用 SharePoint 进行 JSOM 调用。在检索到所有数据之前,我需要获得结果而不是继续前进。我已经尝试了很多示例(none 足够完整,足以让我了解如何使用嵌套的 $.each 循环语句来调整我的问题。我似乎接近了,但从来没有任何工作正常。

我已经编辑了我的实际代码(减去前 3 个变量,因为它们是从另一个页面传入的)以合并 Tomalak 的工作,这样我们就可以更好地解决它。目前,结果是空对象。试图找出我做错了什么。

[2018 年 8 月 6 日编辑] 终于让它工作了。我在提供的代码中只发现了两个小问题 :-)。我会尝试将它们加粗。

var fya = [2008,2009]; //Fiscal Year Array which we use to know what lists to look at
var assignedRecords = []; //Global Reusable Variable
var assignedCourses = ['Math','Science','Reading']; //There might not be records who are associated with a particular course in each list. Wee need to pull the student records (assignedRecords) assoiciated with the above in 2008 and 2009.

SP.ClientContext.prototype.executeQueryPromise = function (items) {
    var result = $.Deferred();
    this.load(items);
    this.executeQueryAsync(function (sender, args) { result.resolve(items) }, 
        function (sender, args) { result.reject(args) });
    return result.promise();
};

将 'var arr = [];' 移出 arrayFromCollection 将 'var' 添加到同一函数中的 e 变量

var arr = [];
function arrayFromCollection(coll) {
    var e = coll.getEnumerator();
    while (e.moveNext()) arr.push(e.get_current());
    return arr;
};

function queryListPromise(title, course) {
    var list = hostWeb.get_lists().getByTitle(title);
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><Query><Where>'
        + '<Eq><FieldRef Name="Course"/><Value Type="Text">' + course + '</Value></Eq>'
        + '</Where></Query></View>');
    return context.executeQueryPromise(list.getItems(camlQuery)).then(arrayFromCollection);
};

function GetAssignedApplications() {
    assignedRecords = []; //Need to start empty
    var myCourses = assignedCourses;

    $.each(myCourses, function (i, myCoursesItem) {
        var courseName = myCoursesItem.get_lookupValue();

将“$.forEach”更改为“$.each”

        $.each(fya, function (n, fyaItem) {
            var listTitle = "FY" + String(fyaItem).substring(2); //FY18 & FY19 are the names of the actual lists.
            assignedRecords.push(queryListPromise(listTitle, courseName));
        });
    });

    $.when(assignedRecords).then(function (results) {
        return Array.prototype.concat.apply([], results);
    }).then(function (items) {
        items.forEach(function (item) {
            var a = item; //item is empty and this actually runs before arrayFromCollection and it returns duplicate records (4) when there is only 2.
        });
    }).fail(onError);
};

首先,等待 jQuery 中的多个承诺是用 $.when() 完成的。

$.when(p1, p2, p3).then(function (results) {
    // results is an array of length 3
});

为了使用可变参数计数使其工作,我们使用 Function#apply:

var promises = [p1, p2, p3]; // could be be of any length now

$.when.apply($, promises).then(function () {
    var results = [].slice.call(arguments);
    // results is an array of the same length as promises
});

为了使其可重用,我们将其命名为 $.whenAll:

$.whenAll = function (promises) {
    return $.when.apply($, promises).then(function () {
        return [].slice.call(arguments);
    });
};

现在我们可以做:

$.whenAll([p1, p2, p3]).then(function (results) {
    // results is an array of length 3
});

接下来,我们需要将callback-based executeQueryAsync() 转换为promise-based 函数。通常,promisification 遵循以下方案:

function somethingPromise(args) {
    var result = $.Deferred();
    somethingAsync(/* onSuccess */ result.resolve, /* onError */ result.reject);
    return result.promise();
}

这需要 onSuccess 和 onError 处理程序接收数据作为参数。 executeQueryAsync 不是这种情况 - 它直接修改 items。但是,我们仍然想用 items 解决承诺,因此我们需要明确地这样做。

让我们将其应用到 SP.ClientContext 原型 right-away 上 re-use。

SP.ClientContext.prototype.executeQueryPromise = function (items) {
    var result = $.Deferred();
    this.load(items);
    this.executeQueryAsync(
        function (sender, args) { result.resolve(items); },
        function (sender, args) { result.reject(args); }
    );
    return result.promise();
};

接下来我们应该设置一个助手,将那些笨重的东西 collections 变成可用的东西。

function arrayFromCollection(coll) {
    var arr = [], e = coll.getEnumerator();
    while (e.moveNext()) arr.push(e.get_current());
    return arr;
}

完成所有这些后,我们可以提取一个函数,该函数采用标题和 returns 项目数组的承诺。

function queryListPromise(title) {
    var list = web.get_lists().getByTitle(title);
    var q = new SP.CamlQuery();
    //...
    return ctx.executeQueryPromise(list.getItems(q)).then(arrayFromCollection);
}

最后,由于您想要一个简单的项目列表,我们应该从一个简单的查询列表开始:

function GetNextFunction() {
    var queries = [];

    // build flat list of queries
    $.each(arr1, function(i, arr1Item) {
        $.each(arr2, function(n, arr2Item) {
            queries.push(queryListPromise(arr2Item));
        });
    });

    // wait on all queries
    $.whenAll(queries).then(function (results) {
        // flatten incoming array of arrays
        return Array.prototype.concat.apply([], results);
    }).then(function (items) {
        // items contains all items from all queries
        items.forEach(function (item) { /* ... */ });
    }).fail(onError);
}

这里 queries 变成了一个扁平的承诺数组,每个承诺将解析为一个项目数组。因此,results 将是项目数组的数组。