【问题标题】:Javascript code running in wrong sequenceJavascript代码以错误的顺序运行
【发布时间】:2017-05-23 22:20:41
【问题描述】:

我有这样的代码:

var newArr = [];
var arr = [{a: 1}, {a:2}, {a:3}, {a:4}];
arr.forEach(function(item) {
  setTimeout(function(){item.a = item.a + 1;}, 3000);
  newArr.push(item.a);
});
console.log(newArr);

由于异步,结果为 [1, 2, 3, 4]。让它记录 [2, 3, 4, 5] 的承诺和回调方式是什么?

【问题讨论】:

    标签: javascript asynchronous callback promise


    【解决方案1】:

    尚不清楚您是否使用超时来替代某些异步行为,或者超时是否与您要解决的问题不可或缺。我们会找到他们两个。我在blog 中写过关于将setTimeout() 转变为承诺模式的文章。因此,首先,将setTimeout() 的使用转变为基于承诺的模式:

    function timeout(delay) {
        return new Promise( 
           (resolve, reject) => setTimeout( resolve, delay )
        )
    } // timeout()
    

    所以,第一步(有点)完成了你想要的:

    arr.forEach(function(item) {
       timeout(3000)
          .then(() => { 
             item.a = item.a + 1; 
             newArr.push(item.a) 
          });
       });
    console.log(arr[0],arr[1],arr[2], arr[3]);  
    console.log(newArr);    
    

    但请注意,forEach() 调用完成后,arrnewArr 立即具有它们的旧值。 最终,它们会被更新。我们什么时候可以使用更新的arrnewArr?我们需要等到值更新。

    var promises=[]
    arr.forEach(function(item) {
    
      promises.push(timeout(3000)
        .then(() => { 
                item.a = item.a + 1; 
                newArr.push(item.a) 
            })
        )
    });
    
    Promise.all(promises).then(() => console.log(arr, newArr))
    

    收集承诺并等待它们全部完成......然后你就知道它们是“最新的”。

    【讨论】:

    • 我想要做的是将功能 item.a = item.a + 1 作为长时间的过程。所以 setTimeout 和函数是分不开的。你必须把它当作一个整体来对待。我想知道的是,是否有办法在调用结果之前执行同步风格的长时间函数(没有承诺或回调,如 item.a = item.a + 1)(newArr.push(item.一))。
    • 我不太清楚你的要求。您在最初的问题中提到您想要一个基于 Promise 的解决方案。我将item.a 的递增保留为延迟过程。如您的问题所示,我的解决方案中的增量发生在 3 秒后。是您想要与item.a++ 分开的newArr.push() 吗?如果是这样,您可以将newArr.push() 移动到Promise.all() 处理程序。
    • 即:将newArr.push()从超时处理程序移动到Promise.all(promises).then(() => { arr.forEach( item => newArr.push(item.a) ) }
    • 我知道这真的很混乱。让我们首先忘记超时。如果有代码 item.a = item.a + 1; newArr.push(item.a);.毫无疑问,我可以得到具有更新值的 newArr。但是,如果 item.a + 1 需要很长时间来处理,则 newArr.push(item.a) 将在 item.a + 1 完成之前执行。因此,我们将获得具有旧值的 newArr。我试图询问如何确保 item.a + 1 始终在执行 newArr.push() 之前完成,即使 item.a + 1 花费很长时间。
    • @zhangjinzhou "item.a + 1 处理时间长,newArr.push(item.a) 会在 item.a + 1 完成之前执行" - 这不是您当前的代码所显示的。如果增量是同步的,则没有问题;如果增量是异步的,它需要像setTimeout 那样进行回调。不管具体情况如何,将异步代码放在一个返回 promise 的函数中。
    【解决方案2】:

    你会这样做:

    创建一个接受两个参数的函数:aarr 以及表示数组的“newArr”以及 a 和 newArr 变量。

    这个函数将作为一个递归函数,它会设置一个超时,然后像这样再次调用自己:

    var arr = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
    var newArr = [];
    var a = 0;
    
    function recursiveIterator(a, arr, newArr) {
      var b = a;
      b++;
      var newerArr = newArr;
      if (b < arr.length) {
        setTimeout(function() {
          newerArr.push(arr[b].a);
          recursiveIterator(b, arr, newerArr);
        }, 1000);
      } else {
        console.log(newerArr);
      }
    }
    
    recursiveIterator(a, arr, newArr);

    【讨论】:

      【解决方案3】:

      摆脱 setTimeout。 REPL

      var newArr = [];
      var arr = [{a: 1}, {a:2}, {a:3}, {a:4}];
      arr.forEach(function(item) {
          item.a = item.a + 1;
          newArr.push(item.a);
      });
      console.log(newArr);
      

      【讨论】:

      • 你打败了我
      • setTimeout 必须在那里。我正在尝试做的是将功能 item.a = item.a + 1 作为长期过程。
      【解决方案4】:

      你应该在超时之前进行增量

      var newArr = [];
      var arr = [{a: 1}, {a:2}, {a:3}, {a:4}];
      var itemsProcessed = 0;
      
      arr.forEach(function(item) {
        setTimeout(function(){item.a = item.a+1;newArr.push(item.a);
        itemsProcessed++;
        if(itemsProcessed === arr.length) {
            console.log(newArr);
          }
        }, 3000);
      });

      【讨论】:

      • setTimeout 必须在那里。我想要做的是将功能 item.a = item.a + 1 作为长期过程。
      • @zhangjinzhou 我已经更新了你的超时功能,请检查。
      • setTimeout 和 item.a = item.a + 1 必须是一个整体。我希望它们以同步方式表示一些长时间的调用。
      【解决方案5】:

      您可以通过nsynjs 同步运行您的代码:

      您的代码如下所示:

      var longRunningFunction = function(ctx,timeout,val) {
          var ret={};
          setTimeout(function(){
              ret.result = val + 1;
              ctx.resume();
          },timeout);
          return ret;
      };
      longRunningFunction.nsynjsHasCallback = true;
      
      function myFunc() {
          var newArr = [];
          var arr = [{a: 1}, {a:2}, {a:3}, {a:4}];
          for(var i=0; i<arr.length; i++) {
              var item=arr[i];
              item.a = longRunningFunction(nsynjsCtx,1000,item.a).result;
              newArr.push(item.a);
          }
          console.log(JSON.stringify(newArr));
      }
      nsynjs.run(myFunc,{},function() {
          console.log('done');
      });
      

      结果输出将延迟 4 秒打印:

      [2,3,4,5]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-17
        • 1970-01-01
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        相关资源
        最近更新 更多