iT邦幫忙

2022 iThome 鐵人賽

DAY 19
1
Modern Web

強化 JavaScript 之 - 程式語感是可以磨練成就的系列 第 19

Day19-JavaScript Promise 系列-更多關於 Promise 的練習

  • 分享至 

  • xImage
  •  

前言

這篇會透過一些練習題講解 Promise 一些可能會漏掉的觀念,更進一步加強 Promise 的觀念。


從幾個練習更了解 Promise

練習題1

我們來將 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,所以印出順序如截圖所示:

練習題2

如果 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

練習題3

以下的範例中,同個 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。

練習題4

題目是要實作 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

JavaScript Promise 類的實作與逐步解說

手写Promise/A+

Implementing Promises In JavaScript

promise-demo

10 JavaScript Promise Challenges Before You Start an Interview

ES6 Promise笔试题


上一篇
Day18-JavaScript Promise 系列-Promise 的幾個靜態方法介紹
下一篇
Day20-非同步處理的方式-async/await
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言