Promise物件代表一個非同步操作最終的完成(或失敗),以及它所產生的值。
對很多人來說,這是個艱深的主題,尤其是當你熟悉的程式語言是完全_同步_的時候。 如果你覺得難以招架,或者想進一步了解並行與平行,可以觀看(透過 go.dev)或直接透過 vimeo 觀看,也可以閱讀投影片,這是場精彩的演講「Concurrency is not parallelism」。
Promise有三種狀態:
當它被建立時,Promise 處於待處理狀態。 在未來的某個時間點,它可能會_解決_或_拒絕_。 一旦 Promise 被解決或拒絕過一次,它就永遠無法再次被解決或拒絕,其狀態也無法改變。
換句話說:
Promise 可以用多種方式解決:
// Creates a promise that is immediately resolved
Promise.resolve(value);
// Creates a promise that is immediately resolved
new Promise((resolve) => {
resolve(value);
});
// Chaining a promise leads to a resolved promise
somePromise.then(() => {
// ...
return value;
});
在上面的範例中,value可以是_任何東西_,包括錯誤、undefined、null或另一個 Promise。
通常你會想用一個不是錯誤的值來解決它。
Promise 可以用多種方式拒絕:
// Creates a promise that is immediately rejected
Promise.reject(reason)
// Creates a promise that is immediately rejected
new Promise((_, reject) {
reject(reason)
})
// Chaining a promise with an error leads to a rejected promise
somePromise.then(() => {
// ...
throw reason
})
在上面的範例中,reason可以是_任何東西_,包括錯誤、undefined或null。
通常你會想用一個錯誤來拒絕它。
Promise 一旦解決或拒絕,就可以用後續的動作_延續_下去。
promise.then()會在promise解決後被呼叫promise.catch()會在promise拒絕後被呼叫promise.finally()會在promise解決或拒絕後被呼叫每個 Promise 都是「thenable」。
這表示會有一個then函式可以使用,它會在原始的 Promise 解決後被執行。
給定promise.then(onResolved),回呼onResolved會收到原始 Promise 被解決時的值。
這永遠會回傳一個_新的_「串接」Promise。
從then回傳一個value會解決「串接」的 Promise。
在then中拋出一個reason會拒絕「串接」的 Promise。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
});
const promise2 = promise1.then(function (value) {
console.log(value);
// expected output: "Success!"
return true;
});
這會在大約 1000 毫秒後記錄"Success!"。
promise1的狀態與值會是resolved和"Success!"。
promise2的狀態與值會是resolved和true。
還有第二個引數可以使用,它會在原始 Promise 拒絕時執行。
給定promise.then(onResolved, onRejected),回呼onResolved會收到原始 Promise 被解決時的值,或者回呼onRejected會收到 Promise 被拒絕的原因。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return true;
}
function shout(reason) {
console.error(reason.toUpperCase());
return false;
}
const promise2 = promise1.then(log, shout);
"Success!"。
promise1的狀態與值會是resolved和"Success!"。promise2的狀態與值會是resolved和true。"NOPE!"。
promise1的狀態與值會是rejected和Nope!。promise2的狀態與值會是resolved和false。重要的是要了解,由於生命週期的規則,當它reject時,約 1000 毫秒後才傳進來的resolve會被默默忽略,因為內部狀態一旦被拒絕或解決就無法再改變。
重要的是要了解,從 Promise 回傳一個值會解決它,而拋出一個值則會拒絕它。
當promise1解決且有一個串接的onResolved:then(onResolved)時,那個後續動作就是一個新的 Promise,可以解決或拒絕。
當promise1拒絕但有一個串接的onRejected:then(, onRejected)時,那個後續動作就是一個新的 Promise,可以解決或拒絕。
有時候你會想捕捉錯誤,並只在原始 Promise reject時繼續。
給定promise.catch(onCatch),回呼onCatch會收到原始 Promise 被拒絕的原因。
這永遠會回傳一個_新的_「串接」Promise。
從catch回傳一個value會解決「串接」的 Promise。
在catch中拋出一個reason會拒絕「串接」的 Promise。
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return 'done';
}
function recover(reason) {
console.error(reason.toUpperCase());
return 42;
}
const promise2 = promise1.catch(recover).then(log);
在大約 1/2 的情況下,這會在大約 1000 毫秒後記錄"Success!"。
在另外 1/2 的情況下,這會立即記錄42。
promise1解決,catch會被略過,接著會到達then,並記錄該值。
promise1的狀態與值會是resolved和"Success!"。promise2的狀態與值會是resolved和"done";promise1拒絕,catch會被執行,它會_回傳一個值_,因此整個串接鏈現在變成resolved,接著會到達then,並記錄該值。
promise1的狀態與值會是rejected和"Nope!"。promise2的狀態與值會是resolved和"done";有時候你會想在 Promise 有結果之後執行程式碼,無論該 Promise 是解決還是拒絕。
給定promise.finally(onSettled),回呼onSettled不會收到任何東西。
這永遠會回傳一個_新的_「串接」Promise。
從finally回傳一個value會複製原始 Promise 的狀態與值,並忽略該value。
在finally中拋出一個reason會拒絕「串接」的 Promise,並覆寫原始 Promise 的任何狀態與值或原因。
把各種方法組合在一起:
const myPromise = new Promise(function (resolve, reject) {
const sampleData = [2, 4, 6, 8];
const randomNumber = Math.round(Math.random() * 5);
if (sampleData[randomNumber]) {
resolve(sampleData[randomNumber]);
} else {
reject('Sampling did not result in a sample');
}
});
const finalPromise = myPromise
.then(function (sampled) {
// If the random number was 0, 1, 2, or 3, this will be
// reached and the number 2, 4, 6, or 8 will be logged.
console.log(`Sampled data: ${sampled}`);
return 'yay';
})
.catch(function (reason) {
// If the random number was 4 or 5, this will be reached and
// reason will be "An error occurred". The entire chain will
// then reject with an Error with the reason as message.
throw new Error(reason);
})
.finally(function () {
// This will always log after either the sampled data is
// logged or the error is raised.
console.log('Promise completed');
});
randomNumber為0-3的情況:
myPromise會以值2, 4, 6, or 8被解決finalPromise會以值'yay'被解決Sampled data: ...Promise completedrandomNumber為4-5的情況:
myPromise會以原因'Sampling did not result in a sample'被拒絕finalPromise會以原因Error('Sampling did not result in a sample')被拒絕Promise completed"uncaught rejected promise: Error('Sampling did not result in a sample')"記錄如上所示,reject可以搭配字串運作,而 Promise 也可以用Error來拒絕。
如果串接 Promise 或一般用法還不清楚,MDN 上的教學是很好的參考資源。