Promise
想像有一位超級歌星,粉絲日以繼夜關心專輯什麼時候出,這位超級歌星很煩惱,因此想出了一個辦法,讓粉絲追蹤他的 IG ,若有任何關於他的消息都可以第一時間知道。 在編寫程式碼也會遇到一樣的問題: 1. 生產程式碼:作一些事需要時間,像是加載腳本,腳色是歌手。 2. 消費程式碼:知道生產程式碼的結果,不論有沒有成功,腳色是粉絲。 3. promise 是連接 2 者的橋樑,生產程式碼的結果會保證傳到每個消費程式碼,腳色是 IG。
// syntax,executor 是生產程式碼,最終會產生結果,傳到 promise,promise 物件有 2 個內部屬性,
// state 描述 promise 狀態,一開始 pedding => fullfilled or rejected
// result 任意值,一開始 undefined => value or error
let promise = new Promise(function(resolve, reject) {
// executor (生产者代码,"singer")
});

// 會自動呼叫 executor,executor 會接收 resolve、reject 2 個參數。
let promise = new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout(() => resolve("done"), 1000);
});

let promise = new Promise(function(resolve, reject) {
// after 1 second signal that the job is finished with an error
setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// executor 只會有一個結果,另一個結果會被忽略。
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
// 可以使用任何類型的參數帶入 reject,但建議使用 Error 物件
// 通常 executor 會執行一些異步程式,這時需要一些時間才能得到結果。我們可以立即返回結果,當任務
// 已經完成。
let promise = new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(123); // immediately give the result: 123
});
// promise 的內建屬性 state and result,不能從消費程式碼取得,但可以透過
// .then/.catch/.finally 等方法取得
Consumers: then, catch, finally
消費程式碼可以得知生產結果,透過 .then
, .catch
and .finally
等方法。
then
// syntax,成功時執行第 1 個參數,失敗時執行第 2 個參數
promise.then(
function(result) { /* handle a successful result */ },
function(error) { /* handle an error */ }
);
// 成功
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
// resolve runs the first function in .then
promise.then(
result => alert(result), // shows "done!" after 1 second
error => alert(error) // doesn't run
);
// 失敗
let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// reject runs the second function in .then
promise.then(
result => alert(result), // doesn't run
error => alert(error) // shows "Error: Whoops!" after 1 second
);
// 只返回成功結果
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // shows "done!" after 1 second
catch
// 只返回失敗結果,可以用 .then(null, errorHandlingFunction) 或 .catch(errorHandlingFunction)
// .catch(f) 是 .then(null, f) 的簡寫
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) is the same as promise.then(null, f)
promise.catch(alert); // shows "Error: Whoops!" after 1 second
finally
// 就像 try {...} catch {...} 有 finally,promise 也有 finally
// .finally(f) 很像 .then(f, f),當 promise 完成他會被執行無論成功或失敗。
// finally 適合使用在清空記憶體
new Promise((resolve, reject) => {
/* do something that takes time, and then call resolve/reject */
})
// runs when the promise is settled, doesn't matter successfully or not
.finally(() => stop loading indicator)
.then(result => show result, err => show error)
// .finally(f) 跟 .then(f, f) 的差別
// 1. finally 沒有參數
// 2. finally 傳遞 result 參數
// 成功
new Promise((resolve, reject) => {
setTimeout(() => resolve("result"), 2000)
})
.finally(() => alert("Promise ready"))
.then(result => alert(result)); // <-- .then handles the result
// 失敗
new Promise((resolve, reject) => {
throw new Error("error");
})
.finally(() => alert("Promise ready"))
.catch(err => alert(err)); // <-- .catch handles the error object
// 3. .finally(f) 比 .then(f, f) 簡潔
// 當 promise 執行完畢結果會立即回傳
// an immediately resolved promise
let promise = new Promise(resolve => resolve("done!"));
promise.then(alert); // done! (shows up right now)
Example: loadScript
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
// 重新改寫
function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Script load error for ${src}`));
document.head.append(script);
});
}
// 使用
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('One more handler to do something else!'));
Promises
Callbacks
Promises allow us to do things in the natural order. First, we run loadScript(script)
, and .then
we write what to do with the result.
We must have a callback
function at our disposal when calling loadScript(script, callback)
. In other words, we must know what to do with the result before loadScript
is called.
We can call .then
on a Promise as many times as we want. Each time, we’re adding a new “fan”, a new subscribing function, to the “subscription list”. More about this in the next chapter: Promises chaining.
There can be only one callback.
Last updated
Was this helpful?