顯示廣告
隱藏 ✕
※ 本文為 dinos 轉寄自 ptt.cc 更新時間: 2015-08-22 15:51:36
看板 Ajax
作者 s25g5d4 (function(){})()
標題 Re: [問題] 延後執行的問題
時間 Sat Aug 22 11:27:47 2015


前陣子因為試用 Visual Studio Code 與 Electron 寫了一個小專案

剛好寫到跟你問題一模一樣的東西,code 在此

https://github.com/s25g5d4/SlideShow/blob/master/scripts/main.js#L76
SlideShow/main.js at master ·  s25g5d4/SlideShow ·  GitHub
[圖]
SlideShow - Full window slideshow for web and Electron(Atom-Shell) ...

 

我使用 mrbigmouth 板友提到的 async.js 套件,你可以參考一下


configs.images 是一個包含數個字串的陣列 (就是圖檔檔名)


loadImg 函數會試著把這些圖檔載入,具體行為是

1. 建立 Image() 並檢查是否有 cache (透過檢查 complete 屬性)

2. 掛上 onload 事件

3. 掛上 onerror 事件


imgDone 函數是當 configs.images 裡所有圖檔都載入後

過濾錯誤的圖片,將頁面初始化並開始繪製畫面

--

若不引入 async.js 套件,可以參考 dianwu 板友的解法

只是不需要多 imgReady 變數

直接檢查 array.length 跟 m.length 就好

另外一個做法是把 dosomething() 改寫成

function dosomething(val, array) {
  array.push(val);

  // 如果圖檔還沒全部載入,就先退出 function
  if (array.length < m.length) return;

  // 若執行到這邊代表圖檔已全部載入
  // 接著做原本該做的事
  // ...
}

原本 onload 事件改成

  img.onload = function () {

//圖片讀取好後進行一些處理再return回來

    var t = doImg(this);
    i.forEach(function(p){

//將圖片及資訊push進array

      dosomething({
        img : t,
        p1 : p,
        p2 : [p[0] + t.width, p[1] + t.height],
        p3 : t.pos[1] + p[1]
      }, array);
    });
  };

--

另外 dianwu 板友提到「不建議在迴圈中直接宣告 function」

我的看法是「不建議使用匿名函數」

這並不是代表不要使用 callback 或 Immediately-invoked function expression
                                 註:即 (function () { /* some code */ }())
而是使用 function expression (以下簡稱 func expr) 時一定要指定函數名稱

在偵錯的時候瀏覽器開發工具或 Node.js 大多會提供 function call stack

如果用一堆匿名函數,錯誤訊息會看得很痛苦

http://craigshoemaker.net/images/command-prompt-node-error.png
[圖]
 
有看到 at <anonymous>:1:55 嗎?

如果 func expr 在內部須呼叫 func expr 本身

使用具名函數便可以以該名稱存取自己

範例:

setTimeout(function doAnimation() {
    // do something
    setTimeout(doAnimation, 100);
}, 100);

而且不會造成任何變數汙染問題

doAnimation 名稱只在 doAnimation 函數內可見

即使在與 setTimeout 同 scope 的地方也不可見 doAnimation

所以不用擔心會蓋掉同樣名稱的變數

--

其實我不知道為什麼 scope 會有問題

除非把 function 提到迴圈所在 scope 外面才會造成 scope 不同

以下兩個例子的 scope 是一樣的

========
array.forEach(function forEach(e, i) {
  // do something
});
========
var doSomething = function doSomething(e, i) {
  // do something
};
array.forEach(doSomething);
=======

在大多數情況下, callback function 應該不需要離 iteration 太遠

頂多拉出來變成另一個變數,這樣還是會在同一個 scope 下啊 XD

如果有必要讓 callback 與 iteration 所在 scope 不同

那應該是這個 callback 可以在多個地方重複使用

這種情況下就需要注意 scope 的問題

javascript 是 static(lexical) scope 的語言

scope 只與 function 定義的地方有關,與執行期 call stack 無關

--
HornyDragon: 昨天看到就秒下載了07/27 22:36
jupto: 好色龍也喜歡人味重的了? 我還以為擬人度高的都不行呢07/27 22:40
ykes60513: 你誤會了 好色龍看上的是那狼人07/27 22:42
HornyDragon: 你看三樓就很懂07/27 22:43

--
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 1.174.152.40
※ 文章代碼(AID): #1Lr-mxn6 (Ajax)
※ 文章網址: https://www.ptt.cc/bbs/Ajax/M.1440214075.A.C46.html
dianwu: 詳細的解說,我印象中如果在迴圈內的function 直接使用上1F 08/22 12:31
dianwu: 層的變數很可能在執行時與一開始的想法有出入,特別又是im
dianwu: g onload,但早上回文時沒有再實做確認一次,也許錯了:)

你說的其實不是 scope 問題

是因為 async call 的關係,抓到的是被覆蓋掉的值

解決方法是建立一個新的 scope, 也就是 closure

var i;
for (i = 0; i < 5; ++i) {
    setTimeout(function () {
        alert(i);
    }, 1);
}

這個範例會彈出五個 5, 而不是預期的 0, 1, 2, 3, 4

var i;
for (i = 0; i < 5; ++i) {
    (function (t) {
        setTimeout(function () {
            alert(t);
        }, 1);
    }(i));
}

這樣才是預期的 0, 1, 2, 3, 4

--

酷一點的解法

var i;
for (i = 0; i < 5; ++i) {
    setTimeout(alert.bind(this, i), 1);
}

--

這應該是講到爛掉的問題了 :P
※ 編輯: s25g5d4 (1.174.152.40), 08/22/2015 13:13:05

--
※ 看板: dinos 文章推薦值: 0 目前人氣: 0 累積人氣: 312 
作者 s25g5d4 的最新發文:
點此顯示更多發文記錄
分享網址: 複製 已複製
guest
x)推文 r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇