Promise - 初探

What is Promise ?

  • Promise 是一個強大的異步執行流程語法結構
  • 並非所有的 Callback 都是異步執行, 使用者自己設計的 Callback 結構當然也非異步, 但可透過一些 API讓使用者設計的 Callback 變成異步, 像是 :
    • 使用計時器(timer)函式: setTimeout, setInterval
    • 特殊的函式: nextTick, setImmediate

Why Promise ?

  • 在執行 Async 事情當錯誤發生時, 可以透過 reject 拋出例外進行對應處理
  • 強大的 chaining 能力(thenable)

How to use Promise ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Asyncfunc(){
return new Promise(function(resolve, reject){
// Do Async things herer
// ...

// if success
resolve(); // 相當於事件成功時的callback
// if occur error
resject(); // 相當於事件失敗時的callback
});
}

Asyncfunc().then(function(){
// 成功時會執行這裡 , 也就是OnFullfilled 函數
}).catch(function(){
// 有error時會執行這裡 , 也就是OnException 函數
});

About .then(…) / .catch(…)

.then(onFullFill)

  • .then() 本身會回傳一個新的promise => 造就了 chainable 的能力
  • onFullFill
    • 第一個參數為上一個 promise resolve(or onFullFill) function 的回傳值
    • 可以回傳三種東西
      • 值 (string, number … etc.)
      • Promise => 會成為這個 .then() 回傳的 Promise
      • thenable object
  • 當 .then() 的第一個參數不是 function 時且 fullfill時, 會忽略這個 .then() (也就是有寫這個 .then() 等於沒寫)

.catch(onReject)

  • 其實就只是 .then(undefined, onReject) 的語法糖而已 (因此特性同上)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function doSomething1() {
console.log("doSomething1 start");
return new Promise(function(resolve, reject) {
console.log("doSomething1 end");
resolve(1);
});
}

function doSomething2() {
console.log("doSomething2");
return 2;
}

function finalThing(value) {
console.log("finalThing");
console.log(value);
return 0;
}

doSomething1()
.then(doSomething2)
.then(finalThing);
1
2
3
4
5
6
7
// Result
doSomething1 start
doSomething1 end
doSomething2
1 // 由 doSomething1 的 resolve(1) 來
finalThing
2 // 由 doSomethng2 的 return 值來

Promise.race()

  • 一群 Promise 比賽, 只回傳第一個完成的 Promise
1
2
3
4
5
6
7
8
9
10
11
12
let racePromise = function(id,timer) {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(`${id} is Complete!`);
}, timer);
});
}

Promise.race([racePromise("01", 1000), racePromise("02", 2000)])
.then((data) => {
console.log(data); // Output : 01 is Complete (因為他比較早完成)
})

Promise.all() - Parallel 執行

  • 一次 Run 一群 Promise, 待全部都完成, 回傳一個存放所有完成結果的陣列
  • 若其中只要有一個Promise reject, 就會算全部失敗, 執行 reject 部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let racePromise = function(id,timer) {
    return new Promise((resolve, reject) => {
    setTimeout(function() {
    resolve(`${id} is Complete!`);
    }, timer);
    });
    }

    Promise.all([racePromise("01", 1000), racePromise("02", 2000)])
    .then((data) => {
    console.log(data);
    // Output : ["01 is Complete!", "02 is Complete!"] (兩秒後才會得到此Output, 因為需等所有Promise完成)
    })
  • 允許部分成功

    1
    2
    3
    4
    5
    6
    7
    ...
    const data = await Promise.all(
    [S_FakeAPICall2s(), S_FakeAPICall1s(), F_FakeAPICall1s()].map(p =>
    p.catch(e => "FAILED!")
    )
    );
    ...
    • 失敗的 Promise (F_FakeAPICall1s()) 會被 chain map 的 catch 接住, 回傳 “FAILED!”, 因此外層 data 仍可已得到一個結果陣列 .

一些觀念

  • .then() 會 return 一個的 Promise 物件, 因此可以 chaining
  • onFullfilled (a.k.a resolve) 可以有三種回傳值
    • value(回隨著新的Promise物件傳下去)
    • Promise物件
    • thenable物件

參考文獻