【问题标题】:How to make an asynchronous loop with callback?如何使用回调进行异步循环?
【发布时间】:2018-08-22 20:09:36
【问题描述】:

我有一个问题,即循环的索引顺序在回调中不断变化。请参阅下面的代码了解我尝试过的内容,我试图实现的目标是根据可用图层添加标记。但是,我遍历图层的顺序必须与添加标记的顺序相同。稍后,当我解决此问题时,目标是用户可以单击生成的标记,这将导致打开一个带有信息的图层。有谁知道解决方案是什么?我已经尝试过@patrickRoberts 和@stian 的cmets。

$(function(){

//  I've tried the following:
//  – async/await with promises
//  – JavaScript Closures
//  – Closures with IIFE (see example below)

'use strict';

//Cache DOM
let $win                = $(window),
    $doc                = $(document),
    $body               = $('body'),
    $layer              = $('.layer'),
    $drawingImage       = $('.drawings__image'),
    $markerContainer    = $('.markers');

//Init
_addMarkers();

function _addMarkers(){
    for(var i = 0; i < $layer.length; i++){
        //Right order
        console.log(i);

        (function(i){
            _calculateScaleFactor($drawingImage.eq(0), function(data){

                //Wrong order
                console.log(i);
            });
        })(i);
    }
}

function _createMarker(x, y, ){
    return $(`<span class="markers__single-marker" style="left:${x}px; top:${y}px;">${i}</span>`);
}

function _calculateScaleFactor(image, callback){
    let newImage    = new Image();

    newImage.src = image.attr('src');
    newImage.onload = function(){
        let scaleFactor = newImage.width / image.width();
        callback(scaleFactor);
    }
}

})

【问题讨论】:

  • 为什么顺序需要一样?您已经有了对索引的引用,因此使用它可能比依赖按顺序调用的回调更容易。
  • 因为否则修正后的 layerData.x 和 layerData.y 不会保持它们的值。
  • 啊,原因是因为你有一个需要正确执行顺序的append() 函数。不幸的是,$.each() 没有尊重异步执行的选项,因此您需要放弃使用它来迭代您的元素,或者您应该在初始异步迭代之后使用它来迭代 scaleFactors 的结果数组已完成。
  • 哦,好吧,让我尝试将这两个功能分开。第一个用于为每个数据创建具有正确 layerData 的数组,第二个用于添加图层标记。如果这能解决我的问题,我会让你。提前致谢!另一方面,你是被雇用的吗?我目前的容量很低,正在寻找额外的双手。
  • 我刚刚尝试将这两个函数分开,但这似乎并不能解决我的问题。我做了一个 for 循环来填充一个带有标记位置的数组,但是仍然由于加载,标记位置的存储顺序不断变化。 @PatrickRoberts 还有其他建议吗?

标签: javascript jquery loops callback closures


【解决方案1】:

经过数小时的试验(完全没有 await/async 经验),我终于用 async 和 await 修复了它。特别感谢@stian!希望该解决方案也对其他人有所帮助,请参见下面的代码:

$(function(){

'use strict';

//Cache DOM
let $win                = $(window),
    $doc                = $(document),
    $body               = $('body'),
    $layer              = $('.layer'),
    $drawingImage       = $('.drawings__image'),
    $markerContainer    = $('.markers');

//Init
_addMarkers();

async function _addMarkers(){
    for(var i = 0; i < $layer.length; i++){
        let _self = $layer.eq(i),
            layerData = {
                x: _self.data('x'),
                y: _self.data('y')
            }
        await _calculateScaleFactor($drawingImage.eq(0)).then(function(resolve){
            layerData.x = (layerData.x / resolve) + $drawingImage.eq(0).offset().left;
            layerData.y = (layerData.y / resolve) + $drawingImage.eq(0).offset().top;

            $markerContainer.append( _createMarker(layerData.x, layerData.y, i) );
            console.log(i);
        });
    }
}

function _createMarker(x, y, i){
    return $(`<span class="markers__single-marker" style="left:${x}px; top:${y}px;">${i}</span>`);
}

function _calculateScaleFactor(image){
    return new Promise(function(resolve, reject){
        let newImage    = new Image();

        newImage.src = image.attr('src');
        newImage.onload = function(){
            let scaleFactor = newImage.width / image.width();
            resolve(scaleFactor);
        }
    });
}

})

【讨论】:

    【解决方案2】:

    这是一个使用 async/await 和 promises 的方法。希望有用。

    $(() => {
      //your array of elements
      let arr = ["hello", "hi", "bye"]
      calculate(arr)
    
    })
    
    async function calculate(arr){
    
      for(let i = 0; i < arr.length; i++){
    
        console.log(i, arr[i])
        let result = await someAsyncStuff(arr[i])
        console.log(i, result)
      }
    
    }
    
    function someAsyncStuff(el){
      return new Promise((resolve, reject) => {
        //your async code here
        setTimeout(resolve, 2000, el)
      })
    }
    

    【讨论】:

    • setTimeout(() =&gt; { resolve(el) }, 2000) -> setTimeout(resolve, 2000, el)
    • 谢谢,我试试看!我会及时通知你。
    • @stian 我尝试了以下方法,但可能我做错了什么。我可以与您分享我的用例代码吗?现在我得到一个“等待仅在异步函数中有效”错误。我对 async/await 函数没有任何经验。
    • 我在理解您要执行的操作时遇到了一些问题。您是否尝试为每个图像获得相同的比例因子?还是您想为每张图像获取一个新的缩放因子?
    • @stian 首先感谢您的帮助!让我简要介绍一下这个项目。我正在循环多个图层(具有内容和数据属性的 div),在这个循环中,我将检查每个图层的 data-x 和 data-y 属性。这些属性包含与图像上的位置相对应的值。因为图像是响应式的,所以我必须计算校正后的 X 和 Y 值。然后为每一层创建一个标记,当用户单击它时显示该层。虽然我还有其他工作要做,但您可以在此处查看示例:futurevisuals.nl/flatland
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-27
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2017-09-09
    相关资源
    最近更新 更多