這篇會透過一些練習題講解 Promise 一些可能會漏掉的觀念,更進一步加強 Promise 的觀念。
我們來將 Day 4 文末的練習題升級一下,讀者可以思考會依序印出什麼?
setTimeout(() => {
console.log('I will be printed out first.');
}, 100);
setTimeout(() => {
console.log('No, I will be printed out faster than you. You need to wait for 100ms.');
}, 0);
const promiseExample = new Promise(function (resolve) {
console.log('Promise executor function will run synchronously');
resolve('But I still faster than you, Mr.SetTimeout. I am a microtask.')
}).then((data) => {
console.log(data);
})
function mockFunc() {
console.log('Shut up! I am a synchronous task so I will run first.');
}
mockFunc();
因為 Promise 的建構函式 new Promise(function(resolve, reject) { ... } )
是同步任務,所以會馬上執行,而 .then()
它裡面的回呼函式是非同步的,並且是屬於 Microtasks,所以印出順序如截圖所示:
如果 Promise 的回呼函式內又有一個 Promise,還有兩個 resolve,會怎麼執行呢?
new Promise(resolve => {
resolve(1);
Promise.resolve().then(() => console.log(2));
resolve(3);
}).then(t => console.log(t));
console.log(4);
兩個印出值(console.log(2)
,console.log(1)
)的任務依序進入 job queue,等同步執行的 4 印出後,才開始執行,而 Promise 執行函式內的 resolve 或 reject 只有第一個會執行,也就是說 Promise 狀態已經改變後就不會再改變,所以不會執行 resolve(3)
。
所以印出的值和順序如下:
4
2
1
以下的範例中,同個 promise 物件呼叫了 .then()
幾次,這樣最後那行 console.log
的結果為何呢?
const promise = new Promise((resolve, reject) => {
resolve(10);
});
promise.then((value) => {
value++;
return value;
});
promise.then((value) => {
value += 10;
return value;
});
promise.then((value) => {
value += 20;
console.log(value);
return value;
});
像上面那樣的撰寫方式並不是 Promise 的鏈式寫法,每次呼叫 then()
都是獨立的一件事,不會互相影響,所以最後印出 30。
題目是要實作 doByOrder 函式,且執行函式後可以印出 ['a', 'b', 'c']
。
const a = callback => {
setTimeout(() => { callback('a') }, 900);
}
const b = callback => {
setTimeout(() => { callback('b') }, 600);
}
const c = callback => {
setTimeout(() => { callback('c') }, 300);
}
const tasks = [a, b, c];
function doByOrder(tasks, fn) {
// TODO
}
doByOrder(tasks, console.log.bind(console))
三個非同步的任務,如果僅僅只是依序的去執行它們,會從時間少的開始執行,變成依序印出 c, b, a
function doByOrder(tasks, fn) {
tasks.forEach((task) => task(fn)); // 錯誤解法
}
那就將每個任務轉成 Promise,並用陣列存起來,到時用 Promise.all() 將結果呈現即可:
function doByOrder(tasks, fn) {
const promiseArr = tasks.map((task) => {
return new Promise((resolve, reject) => {
// ?
});
})
}
至於註解的部分要填入什麼程式碼,可以回去看昨天關於 Promise.all()
的範例,以下擷取片段,並搭配著任一個題目的執行任務來看。
function getPromise(i) {
return new Promise((resolve) => {
setTimeout(() => resolve(i), Math.random() * 5000);
})
}
// 題目要處理的任務
const a = callback => {
setTimeout(() => { callback('a') }, 900);
}
// 結合 getPromise 程式碼片段就可以知道 a callback 函式可以改寫成以下程式碼
const a = resolve => {
setTimeout(() => { resolve('a') }, 900);
}
那看來我們可以把 resolve 當作函式傳入,題目就解出來了。
function doByOrder(tasks, fn) {
const promiseArr = tasks.map((task) => {
return new Promise((resolve, reject) => {
task(resolve);
});
})
Promise.all(promiseArr).then((value) => console.log(value));
}
讀者的解法是什麼呢?也歡迎留言分享~
Promise 系列就到這邊啦~Promise 真的蠻重要的,希望讀者看完能有所收穫,對內容有建議或是想討論的地方也歡迎留言,感謝!
另外其實本來還想寫關於 Promise 的實作文章,但查了些資料還是沒辦法明確的用文字敘述並寫成一篇文章,大概就算是似懂非懂的狀態,所以以下放了幾個我推薦的資料給讀者閱讀。
How To Create Your Own Implementation Of JavaScript Promises
Implementing Promises In JavaScript
10 JavaScript Promise Challenges Before You Start an Interview