2017-08-29 45 views
0

我正在製作一個簡單的html5遊戲。對象鍵映射 - 如何暫停迭代

 Object.keys(gameConfig.playerElems).map((e) =>{ 

     let img = gameConfig.playerElems[e]; 
     let name = e; 
     let imgObj; 

     imgObj = new Image(); 
     imgObj.src = img; 

     imgObj.onload =() => { 
      playerElemsCounter++; 
      drawPlayer(imgObj); 
     } 
    }); 

是否有可能暫停.map()迭代而imgObj將被載入?

+0

你不能。你可以使用遞歸來實現這個 – Rajesh

+0

這是不可能的,因爲即使圖像被加載,JS在事件循環返回之前也不會觸發事件。您將不得不以異步方式重新設計您的代碼。 –

+2

通過一些小的調整,這個問題中解決方案的ES6部分也可以工作:[在所有異步forEach回調完成後回調](https://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach - 完成) – Andreas

回答

2

imgObj將被加載時是否可以暫停.map()迭代?

不是,所以您使用異步循環。這裏有一個例子,看評論:

// A named IIFE 
(function iteration(keys, index) { 
    // Get info for this iteration 
    let name = keys[index]; 
    let img = gameConfig.playerElems[name]; 
    let imgObj = new Image(); 
    // Set event callbacks BEFORE setting src 
    imgObj.onload =() => { 
     playerElemsCounter++; 
     drawPlayer(imgObj); 
     next(); 
    }; 
    imgObj.onerror = next; 
    // Now set src 
    imgObj.src = img; 

    // Handles triggering the next iteration on load or error 
    function next() { 
     ++index; 
     if (index < keys.length) { 
      iteration(keys, index); 
     } 
    } 
})(Object.keys(gameConfig.playerElems), 0); 

,作爲Haroldo_OK指出,這將等待一個圖像請求下一個,這不僅沒有必要,而且有害之前加載。相反,請求全部,在您收到它們時繪製它們,然後繼續。你可能做到這一點的給自己的加載函數返回一個承諾:

const loadImage = src => new Promise((resolve, reject) => { 
    const imgObj = new Image(); 
    // Set event callbacks BEFORE setting src 
    imgObj.onload =() => { resolve(imgObj); }; 
    imgObj.onerror = reject; 
    // Now set src 
    imgObj.src = src; 
}); 

然後:

// Load in parallel, draw as we receive them 
Promise.all(Object.keys(gameConfig.playerElems).map(
    key => loadImage(gameConfig.playerElems[key]) 
      .then(drawPlayer) 
      .catch(() => drawPlayer(/*...placeholder image URL...*/)) 
) 
.then(() => { 
    // All done, if you want to do something here 
}); 
// No need for `.catch`, we handled errors inline 

如果(出於某種原因)撐起加載下一個圖像,同時等待以前認爲,loadImage功能可以用不同的方式做到這一點,例如經典承諾reduce模式:

// Sequential (probably not a good idea) 
Object.keys(gameConfig.playerElems).reduce(
    (p, key) => p.then(() => 
        loadImage(gameConfig.playerElems[key]) 
        .then(drawPlayer) 
        .catch(() => drawPlayer(/*...placeholder image URL...*/)) 
       ) 
    , 
    Promise.resolve() 
) 
.then(() => { 
    // All done, if you want to do something here 
}); 
// No need for `.catch`, we handled errors inline 

...或ES2017 async/await

// Sequential (probably not a good idea) 
(async function() { 
    for (const key of Object.keys(gameConfig.playerElems)) { 
     try { 
      const imgObj = await loadImage(gameConfig.playerElems[name]); 
      playerElemsCounter++; 
      drawPlayer(imgObj); 
     } catch (err) { 
      // use placeholder 
      drawPlayer(/*...placeholder image URL...*/); 
     } 
    } 
})().then(() => { 
    // All done 
}); 
// No need for `.catch`, we handled errors inline 

邊注:有沒有必要使用map如果你不是A) 從回調返回一個值,用於填充新數組map創建,B) 使用數組map返回。當你不這樣做時,只需使用forEach(或forfor-of循環)。

+1

另一種方法是要求瀏覽器一次加載所有圖像,然後等待所有'onload'事件發生。這將具有如下優點:如果一個特定圖像的加載速度比其他圖像慢,則不會阻止該隊列。 –

+1

@Haroldo_OK:這將是一個更好的方式來做到這一點。在我對OP代碼的解釋中,我非常直白,不是嗎?衛生署! –

+0

@Andreas的評論更接近我將如何實現它(用'Promise.all()'):https://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach-callbacks-are-完成 –