setTimeout Promise,wait all setTimeout callback function over
前言
Javascript是單一執行緒程式,它沒辦法像C#那樣開多執行緒並同時間處理事情
如果在程式裡寫下
<html>
<body>
<!--引用jQuery-->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js" ></script>
<script type="text/javascript">
$(function () {
setTimeout(function () {
console.log("執行setTimeout裡的function1");
}, 0);
setTimeout(function () {
console.log("執行setTimeout裡的function2");
}, 0);
console.log("main thread done!");
});
</script>
</body>
</html>
類似setTimeout(或$.ajax() )這種有callback function的執行
Javascript會先把callback function 佇列起來,等待主執行緒全部執行完畢後,再一一執行佇列裡的callback function
所以執行結果↓
以上話句話說,如果主執行緒沒執行完畢(例如跑了無窮迴圈),callback function就不會被執行
有興趣的人可自行在console.log("main thread done!")前面加一行while(true){}試試
setTimeout()和$.ajax又有點微妙地不同,setTimeout()只有callback function,$.ajax()則會先發出request而callback function則等待主執行緒執行完畢才執行
不過本文要討論的是setTimeout(),最近工作上有需求,得等待所有setTimeout()的callback function執行完畢,再執行主執行緒後續的程式
原以為setTimeout()會像$.ajax()一樣回傳Promise物件,所以寫了以下錯誤寫法Orz
<html>
<body>
<!--引用jQuery-->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function () {
//Promise集合
let myPromises = new Array();
let p1 =
setTimeout(function () {
console.log("執行setTimeout裡的function1");
}, 500);
myPromises.push(p1);
let p2 =
setTimeout(function () {
console.log("執行setTimeout裡的function2");
}, 100);
myPromises.push(p2);
//等待所有setTimeout callback function執行完畢才執行
$.when.apply(undefined, myPromises).then(function () {
//let args = arguments;
console.log("main thread done!");
});
});
</script>
</body>
</html>
執行結果不是預期我想要的(setTimeout callfunction 都執行完畢再執行「main thread done!」)
實作
上網找到jQuery官方文件才知道要把setTimeout()包裝成Promise物件才行
<html>
<body>
<!--引用jQuery-->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function () {
//Promise集合
let myPromises = new Array();
let promiseTemp = {};//暫存變數
let p1 = function () {
//建立Deferred物件
let dfd = jQuery.Deferred();
setTimeout(function () {
console.log("執行setTimeout裡的function1");
//標註成功
dfd.resolve();
}, 500);
//回傳promise
return dfd.promise();
};
promiseTemp = p1();//執行並取得Promise物件
myPromises.push(promiseTemp);
let p2 = function () {
//建立Deferred物件
let dfd = jQuery.Deferred();
setTimeout(function () {
console.log("執行setTimeout裡的function2");
//標註成功
dfd.resolve();
}, 100);
//回傳promise
return dfd.promise();
};
promiseTemp = p2();//執行並取得Promise物件
myPromises.push(promiseTemp);
//等待所有setTimeout callback function執行完畢才執行
$.when.apply(undefined, myPromises).then(function () {
//let args = arguments;
console.log("main thread done!");
});
});
</script>
</body>
</html>
執行結果如預期我想要的,setTimeout callback function全都執行完畢,才執行「main thread done!」
如果工作專案不在乎非同步函數執行順序的話,其實這樣打完就可以收工
但如果想要非同步函數有順序地逐一執行而且用戶可能使用IE11瀏覽器(也就是程式無法使用ES7規格的async、await)
程式碼得改寫如下:
<html>
<body>
<!--引用jQuery-->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function () {
//Promise集合
let myPromises = new Array();
let p1 = function () {
//建立Deferred物件
let dfd = jQuery.Deferred();
setTimeout(function () {
console.log("執行setTimeout裡的function1");
//標註成功
dfd.resolve();
}, 500);
//回傳promise
return dfd.promise();
};
myPromises.push(p1);
let p2 = function () {
//建立Deferred物件
let dfd = jQuery.Deferred();
setTimeout(function () {
console.log("執行setTimeout裡的function2");
//標註成功
dfd.resolve();
}, 100);
//回傳promise
return dfd.promise();
};
myPromises.push(p2);
waitAllAsyncFunc(myPromises, function () {
console.log("main thread done!");
});
});
//所有非同步function按照順序地逐一執行
function waitAllAsyncFunc(myPromises, allDoneFunc) {
if (myPromises !== undefined && myPromises !== null && myPromises.length > 0) {
//等待第一個setTimeout callback function執行完畢才執行下一個setTimeout callback function
$.when(myPromises[0]()).then(function () {
//let args = arguments;
myPromises.splice(0, 1);
waitAllAsyncFunc(myPromises, allDoneFunc)
});
} else {
allDoneFunc();
}
}
</script>
</body>
</html>
執行結果↓
結語
本文主要記錄幾個重點:
1.把setTimeout函數包裝成Promise
2.使用$.when()等待所有非同步函數執行完畢
3.如何有順序地執行非同步函數