# Promise
Promise 物件是一個代表即將完成或失敗的非同步操作,和它產生結果的值 (resolve, reject)
Promise 的出現是為了解決非同步過度巢狀的問題,也就是常聽到的 callback hell 的問題。
忘記的朋友看到下面這張圖就應該能勾起回憶了吧 😆

## Promise 語法:
```
new Promise( /* executor */ function(resolve, reject) { ... } );
```
executor 為依序接收兩個參數的函式: resolve 及 reject(實現及拒絕回呼函式)
Promise 物件會處於以下三種狀態之一:
* 擱置(pending):初始狀態,不是 fulfilled 與 rejected。
* 實現(fulfilled):表示操作成功地完成。
* 拒絕(rejected):表示操作失敗了。
```
function callback(resolve, reject){
// Async operation , network call, disk read I/O
// Async non-block language
setTimeout(()=> resolve(), 1000)
}
const promise = new Promise(callback)
promise.then(() => console.log("hello"))
.catch(() => console.log("rejected"))
```
如果 resolve, reject 都沒有使用,只會console.log 出hello
.then/.catch/.finally 就都不會被處理,promise 狀態就會永遠停在 pending 狀態。
```
function callback(resolve, reject) {
console.log("hello")
}
const promise = new Promise(callback)
promise.then(() => console.log("hello"))
.catch(() => console.log("rejected"))
.finally(()=> console.log("Promise completed"))
```
## Async / Await 的出現是為了什麼 ?
它是Promise 的語法糖,也就是基於 Promise 的結構上,Async / Await 讓非同步的 Promise 可以更直觀的被使用,新手也可以容易上手的非同步程式!
不過還是要好好了解 Promise ,才可以真正了解 Async / Await。
當 async function 被呼叫時,它會回傳一個 Promise 物件
回傳值是一個 Promise 可以是 即將 resolved 的非同步函式,也可以是被例外拋出錯誤的 rejected。
await 表達式會暫停執行程式碼,確保等到 Promise return 值是 fulfilled 或 rejected ,讓程式碼執行的向同步函式一樣。
如果 async 函式回傳一個值 沒有明確寫是 Promise,它也會是隱藏式的被包在 Promise 中。
例如:
```
async function foo() {
return 1;
}
```
相近於以下程式碼
```
function foo() {
return Promise.resolve(1);
}
```
如果有回傳值,第二個 async function Promise 如果 resovle 成功會回傳hello => 使用 then 來接著,若成功回傳 resolve 值 “hello”
`hello().then((res) => console.log(res)) // hello`
```
function hello (){}
console.log(hello()) // undefined
async function hello(){}
console.log(hello()) // Promise {undefined}
function hello() {return "hello"}
console.log(hello()) // hello
async function hello(){return "hello"}
console.log(hello()) // Promise {'hello'}
hello().then((res) => console.log(res)) // hello
```
## LeetCode 練習題
來試寫兩題 LeetCode 的 Promise 題目吧!
Sleep 比較簡單, Promise Time Limit 比較複雜一點,但兩個都是歸類在easy 的題目。
要看完整題目內容的朋友可以點進去連結裡
### [2621. Sleep](https://leetcode.com/problems/sleep/description/)
Given a positive integer millis, write an asynchronous function that sleeps for millis milliseconds. It can resolve any value.
給定一個正整數 millis,寫一個非同步函式在經過睡眠 millis milliseconds, 它可以 resolve 任何值。
Example 1:
```
Input: millis = 100
Output: 100
Explanation: It should return a promise that resolves after 100ms.
let t = Date.now();
sleep(100).then(() => {
console.log(Date.now() - t); // 100
});
```
Example 2:
```
Input: millis = 200
Output: 200
Explanation: It should return a promise that resolves after 200ms.
```
**思路: 要寫一個非同步的 sleep 函式,參數會帶入 millis 代表多少秒以後要轉成 fulfilled 的狀態,並且可以resolve任何值。**
看到 async function 和 resolve 就知道要 return new Promise。
在 executor 放入 resolve ,設定 setTimeout 在 millis 毫秒後 resolve( )。
= > 不放任何參數代表可以 resolve 任何值
因為這裡不需要接住 error,可以不用放 reject。
以下為 解答:
```
async function sleep(millis) {
return new Promise((resolve) =>{
setTimeout(() => {
resolve()
}, millis)
})
}
/**
* let t = Date.now()
* sleep(100).then(() => console.log(Date.now() - t)) // 100
*/
```
而下一題,我們會介紹兩個寫法 Promise 和 Promise.race( )
先來看看題目! 這題非常的實用~
### [2637. Promise Time Limit](https://leetcode.com/problems/promise-time-limit/description/)
Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input function. fn takes arguments provided to the time limited function.
給一個非同步函式 fn 和 t 毫秒時間,會回傳一個 input 函式有新的時間限制版本。 fn 會接受限時函式的 arguments 參數。
The time limited function should follow these rules:
If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result.
If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded".
限時函式應有以下規則:
.如果 fn 在 t 毫秒內完成, 限時函式要 resolve 結果
.在處理 fn 時超過了時間限制,限時函式要 reject 並回傳字串"Time Limit Exceeded" 。
Example 1:
```
Input:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 50
Output: {"rejected":"Time Limit Exceeded","time":50}
Explanation:
const limited = timeLimit(fn, t)
const start = performance.now()
let result;
try {
const res = await limited(...inputs)
result = {"resolved": res, "time": Math.floor(performance.now() - start)};
} catch (err) {
result = {"rejected": err, "time": Math.floor(performance.now() - start)};
}
console.log(result) // Output
The provided function is set to resolve after 100ms. However, the time limit is set to 50ms. It rejects at t=50ms because the time limit was reached.
```
Example 2:
```
Input:
fn = async (n) => {
await new Promise(res => setTimeout(res, 100));
return n * n;
}
inputs = [5]
t = 150
Output: {"resolved":25,"time":100}
Explanation:
The function resolved 5 * 5 = 25 at t=100ms. The time limit is never reached.
```
Example 3:
```
Input:
fn = async (a, b) => {
await new Promise(res => setTimeout(res, 120));
return a + b;
}
inputs = [5,10]
t = 150
Output: {"resolved":15,"time":120}
Explanation:
The function resolved 5 + 10 = 15 at t=120ms. The time limit is never reached.
```
Example 4:
```
Input:
fn = async () => {
throw "Error";
}
inputs = []
t = 1000
Output: {"rejected":"Error","time":0}
Explanation:
The function immediately throws an error.
```
Constraints:
```
0 <= inputs.length <= 10
0 <= t <= 1000
fn returns a promise
```
起手式:
```
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
var timeLimit = function(fn, t) {
return async function(...args) {
}
};
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
```
**思路:**
1. 我要去處理 resolve 和 reject 分別的結果,那我會需要 return new Promise,這樣就可以去處理 題目要求的 fn 完成或失敗的結果。
2. fn 會接收 arguments 參數, return 一個 Promise, t 毫秒內完成就 resolve(result),若沒有完成則 reject(“Time Limit Exceeded”)。
3. 在最後,我們把每一個 setTimeout 清除掉。沒有刪掉也是可以運行,但是用 clearTimeout 可以減少內存溢出 (out of memeory) 的風險。
```
var timeLimit = function(fn, t) {
return async function(...args) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject("Time Limit Exceeded")
}, t)
fn(...args)
.then((res) => resolve(res))
.catch((err) => reject(err))
// finally() 不管失敗或成功都要執行的函式
.finally(() => clearTimeout(timeoutId))
})
}
};
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
```
若是使用 Promise.race() 就更簡潔~
Promise.race(iterable) 方法回傳一個 Promise 物件,這個 Promise 物件裡帶進的 iterable 參數(例如 array),任一個 promise 轉為 resolve 或 rejected 時也會跟著轉變,並接收其成功或失敗的訊息。
**思路:**
要用兩個Promise 函式給 Promise.race,一個是 resolve 成功要執行的,一個是 rejected 失敗要執行的。
```
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
var timeLimit = function(fn, t) {
return async function(...args) {
const originalFnPromise = fn(...args)
const rejectedPromise = new Promise((_, reject) =>{
setTimeout(() => {
reject("Time Limit Exceeded")
}, t)
})
return Promise.race([originalFnPromise, rejectedPromise])
}
};
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
```
補充 Promise.all() 筆記:
Promise.all()方法將可迭代的 Promise 作為 input 並返回單個Promise.
當所有輸入的 Promise 都實現時(包括傳遞一個空的可迭代對象時),這個返回的 Promise 就會實現,並回傳一個包含這些 fulfillment 被實現的值的陣列。
若其中有一個 Promise 被 rejected,Promise.all() 回傳的 promise 也是會被 rejected,會回傳失敗的結果值。
---
好的~ Promise 系列目前就到這邊啦~ 日後有新增修改再來補上
若有發現任何錯誤的地方,也請麻煩路過的大大指點 🙏
---
想要看中文版 youtuber 的影片文軒解碼有這一系列的影片還不錯~
[【零基础JavaScript教程】#14 JavaScript Promise 是什么?异步(非同步)编程必须掌握的技巧 程序员终于可以逃出回调地狱callback hell了!!](https://www.youtube.com/watch?v=CTChl_DYTz0)
[【JavaScript教程】#15 轻松入门 JavaScript Async Await | Async Await 比 Promise 更好用?让你快速掌握 async await 异步执行概念
](https://www.youtube.com/watch?v=zoZiQJ38bXk&t=627s)
**參考資料:**
[MDN Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise)
[MDN Async / Await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
[MDN Promise.race( )](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/race)
[MDN Promise.all( )](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
[LeetCode 2621 sleep](https://leetcode.com/problems/sleep/description/)
[LeetCode 2637. Promise Time Limit](https://leetcode.com/problems/promise-time-limit/description/)
[Promise Time Limit — Leetcode 2637 — JavaScript 30-Day Challenge](https://www.youtube.com/watch?v=hfH57rdZhOk&t=528s)