同步循环中的Javascript承诺

时间:2017-05-17 21:08:30

标签: javascript arrays loops promise tableau

我的任务相当简单,但却让我脱掉了头发。已经搜索了整个互联网,找不到任何解决方案可以直接翻译成我的问题。 这是JavaScript - no return

的后续问题

这是我得到的:

var worksheetArray;
var filtersArray =[];

function testu(){

  filtersArrayFetch();
  console.log("finished fetching");
  console.log(filtersArray);
  //do more stuff with array

}

function filtersArrayFetch()
{

    workbook = viz.getWorkbook();
    sheet=viz.getWorkbook().getActiveSheet();
    worksheetArray = sheet.getWorksheets();


        for (var i=0; i<worksheetArray.length; i++) {
          (function(num){
            worksheetArray[i].getFiltersAsync()
                .then(function(promise){
                    for (j=0;j<promise.length;j++)
                    {
                        filtersArray.push(promise[j].getFieldName());
                    }
               })

          })(i);  

         }
console.log("after for");

}

简单的英语 - 我有一个使用同步Tableau API函数获取的工作表数组。 getFiltersAsync()将Promise作为数组返回。在这个数组上,我想执行getFieldName并将其推送到最终的数组,我稍后会在代码中使用它。代码一直工作到worksheetArray[i].getFiltersAsync(),但不评估.then()并将undefined返回到testu()函数。按钮单击调用testu()。 我需要保持IE(Edge)兼容性,因此Promise.all()不是一个选项。

脚本有什么问题?为什么不评估.then()

编辑:

我设法使用自调用转轮功能来解决问题:

function filtersearch2(i){
    workbook = viz.getWorkbook();
    sheet=viz.getWorkbook().getActiveSheet();
    worksheetArray = sheet.getWorksheets();

    var filtersArray=[];
    var d=$.Deferred();

    (function runner(i){ 

        worksheetArray[i].getFiltersAsync().then(
        function(filtersArrayReturnedInPromise){

        for (z=0;z<filtersArrayReturnedInPromise.length;z++){
            field = filtersArrayReturnedInPromise[z].getFieldName();

            if (field.search("AutoFilter")>0 && field.search("Action")==-1 ){
                filtersArray[filtersArray.length]=field; 
            }

        }

        if (i==worksheetArray.length-1){
            var uniq = filtersArray.reduce(function(a,b){
            if (a.indexOf(b) < 0 ) a.push(b);
            return a;
                },[]);

        console.log("resolving filtersearch");
        d.resolve(uniq);

        } else {

            i++;
            runner(i);
        }

        });

    })(i)

return d.promise();
}

1 个答案:

答案 0 :(得分:0)

请使用/ declare(local)变量并停止污染全局命名空间。

我已经重写了您的代码以正确处理承诺。

filtersArrayFetch()必须返回一个promise,以便其他函数可以处理其计算结果。并且testu()需要使用此承诺才能确定何时完成filtersArrayFetch

在尝试处理/记录filtersArray之前,您的代码并没有等待承诺解决。而你在promises中修改的全局/共享filtersArray的方法本身就非常危险/不可预测。把这个模式带到你调用两次的函数中,所有的地狱都会破碎;你不仅要撕掉你的头发,在试图重现某些结果或调试可能产生的混乱时,它会使你的头部着火。
因为大多数代码都不知道某些随机承诺何时完成并开始向该数组添加项目;这些项目来自哪个函数。

function testu(){
    filtersArrayFetch().then(filtersArray => {
        console.log("finished fetching");
        console.log(filtersArray);      
        //do more stuff with array
    });
}

function filtersArrayFetch() {
    //a few functions that define some tasks/steps that need to be done 
    //in order to do the whole task.

    //Array<Array<*>> -> Array<*>
    var flatten = arrays => [].concat(...arrays);

    //filter -> fieldName
    var getFieldName = filter => filter.getFieldName();

    //Array<filter> -> Array<fieldName>
    var filtersToFieldNames = filters => filters.map(getFieldName);

    //worksheet -> Promise<Array<fieldName>>
    var worksheetToFieldNames = worksheet => worksheet.getFiltersAsync().then(filtersToFieldNames);

    //telling by your code, to this point everything should be sync.
    var worksheets = viz.getWorkbook().getActiveSheet().getWorksheets();

    return Promise.all(worksheets.map(worksheetToFieldNames)).then(flatten);
}

worksheets.map(worksheetToFieldNames)Array<worksheet>转换为Array<Promise<Array<fieldName>>>。通过Promise.all()运行后,我们有一个Promise<Array<Array<fieldName>>>。数组数组的承诺;为每个工作表提供一个字段名数组。

通过.then(flatten)执行此操作后,我们有一个简单的Promise<Array<fieldName>>

但是从我们开始处理承诺的角度来看,我们将不得不继续处理承诺。我们无法打开值并再次进行同步。所以这个函数唯一可以返回的是Promise (无论如何)

相关问题