JavaScript promise是一個對象,表示異步任務完成或者失敗及其結果值。javascript
完結。java
我固然是開玩笑的。那麼,這個定義到底意味着什麼?git
首先,JavaScript
中的許多東西都是對象。你能夠經過幾種不一樣的方式進行建立對象。最經常使用的方法是使用對象字面量語法:github
const myCar = {
color: 'blue',
type: 'sedan',
doors: '4',
};
複製代碼
你還能夠建立一個類
,並經過new
關鍵字對其進行實例化。數據庫
class Car {
constructor(color, type, doors) {
this.color = color;
this.type = type;
this.doors = doors
}
}
const myCar = new Car('blue', 'sedan', '4');
複製代碼
console.log(myCar);
複製代碼
promise
只是咱們建立的對象,就像後面的例子同樣,咱們使用new
關鍵字對其進行實例化。咱們傳入一個帶有兩個參數的函數,其參數爲resolve
和reject
,而不是像傳遞給咱們Car
的三個參數(顏色,類型和門)。promise
最終,promise
告訴咱們一些關於咱們從它返回的異步函數的完成狀況--生效了或失敗了。咱們認爲這個功能是成功的,若是promise
是解決了,而且說promise
被拒絕是不成功的。服務器
const myPromise = new Promise(function(resolve, reject) {});
複製代碼
console.log(myPromise);
複製代碼
留意,此時的promise是pending狀態網絡
const myPromise = new Promise(function(resolve, reject) {
resolve(10);
});
複製代碼
留意,咱們用10返回解決了promiseapp
看,不是太可怕 -- 只是咱們建立的對象。並且,若是咱們稍微展開一下:dom
留意,咱們有一些咱們能夠訪問的方法,即"then"和"catch"
此外,咱們能夠傳咱們喜歡的東西到resolve
和reject
中。例如,咱們能夠傳遞一個對象,而不是一個字符串:
return new Promise((resolve, reject) => {
if(somethingSuccesfulHappened) {
const successObject = {
msg: 'Success',
data,//...some data we got back
}
resolve(successObject);
} else {
const errorObject = {
msg: 'An error occured',
error, //...some error we got back
}
reject(errorObject);
}
});
複製代碼
或者,爲了方便查看,咱們任何東西都不傳:
return new Promise((resolve, reject) => {
if(somethingSuccesfulHappend) {
resolve()
} else {
reject();
}
});
複製代碼
JavaScript
是單線程的。這意味着它一次只能處理一件事。想象這麼條道路,你能夠將JavaScript
視爲單車道的高速公路。特定代碼(異步代碼)能夠滑動到一邊,以容許其餘代碼越過它。完成異步代碼後,它將返回到道路。
旁註,咱們能夠從任何函數返回
promise
。他沒必要是異步的。話雖這麼說,promise
一般在它們返回的函數是異步的狀況下返回。例如,具備將數據保存在服務器的方法API將是返回promise
的絕佳候選者!
外號:
promise
爲咱們提供了一種等待異步代碼完成,從中捕獲一些值,並將這些值傳遞給程序其餘部分的方法。
我這裏有篇文章深刻探討這些概念:Thrown For a Loop: Understanding Loops and Timeouts in JavaScript。
使用promise
也稱爲消費promise
。在上面的示例中,咱們的函數返回了一個promise
對象。這容許咱們使用方法的鏈式功能。
我打賭你看到過下面的這種鏈式方法:
const a = 'Some awesome string';
const b = a.toUpperCase().replace('ST', '').toLowerCase();
console.log(b); // some awesome ring
複製代碼
如今,(僞裝)回想下咱們的promise
:
const somethingWasSuccesful = true;
function someAsynFunction() {
return new Promise((resolve, reject){
if (somethingWasSuccesful) {
resolve();
} else {
reject()
}
});
}
複製代碼
而後,經過鏈式方法調用咱們的promise
:
someAsyncFunction
.then(runAFunctionIfItResolved(withTheResolvedValue))
.catch(orARunAfunctionIfItRejected(withTheRejectedValue));
複製代碼
想象一下,你有一個從數據庫中獲取用戶的功能。我在codepen上編寫了一個示例函數,用於模擬你可能使用的API。它提供了兩種訪問結果的選項。一,你能夠提供回調功能,在其中訪問用戶或提示錯誤。或者第二種,函數返回一個promise
做爲用戶訪問或提示錯誤的方法。
爲了方便查看,我把做者的codepen上的代碼複製了下來,以下:
const users = [
{
id: '123',
name: 'John Smith',
posts: [
{title: 'Some amazing title', content: 'Here is some amazing content'},
{title: 'My favorite title', content: 'My favorite content'},
{title: 'A not-so-good title', content: 'The not-so-good content'},
]
},
{
id: '456',
name: 'Mary Michaels',
posts: [
{title: 'Some amazing title', content: 'Here is some amazing content'},
{title: 'My favorite title', content: 'My favorite content'},
{title: 'A not-so-good title', content: 'The not-so-good content'},
]
},
]
function getUserPosts(id, cb) {
const user = users.find(el => el.id === id);
if (cb) {
if (user) {
return cb(null, user);
}
return cb('User Not Found', null);
}
return new Promise(function(resolve, reject){
if (user) {
resolve(user);
} else {
reject('User not found');
}
});
}
/* The above code is collapsed to simulate an API you might use to get user posts for a * particular user from a database. * The API can take a callback as a second argument: getUserPosts(<id>, <callback>); * The callback function first argument is any error and second argument is the user. * For example: getUserPosts('123', function(err, user) { if (err) { console.log(err) } else { console.log(user); } }); * getUserPosts also returns a promise, for example: getUserPosts.then().catch(); * The ID's that will generate a user are the of type string and they are '123' and '456'. * All other IDs will return an error. */
getUserPosts('123', function(err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
}
});
getUserPosts('129', function(err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
}
});
getUserPosts('456')
.then(user => console.log(user))
.catch(err => console.log(err));
複製代碼
傳統上,咱們將經過使用回調來訪問異步代碼的結果。
rr someDatabaseThing(maybeAnID, function(err, result)) {
//...Once we get back the thing from the database...
if(err) {
doSomethingWithTheError(error)
} else {
doSomethingWithResults(results);
}
}
複製代碼
在它們變得過分嵌套以前,回調的使用是能夠的。換句話說,你必須爲每一個新結果運行更多異步代碼。回調的這種模式可能會致使「回調地獄」。
Promise
爲咱們提供了一種更優雅,更易讀的方式來查看咱們程序流程。
doSomething()
.then(doSomethingElse) // and if you wouldn't mind
.catch(anyErrorsPlease);
複製代碼
想象一下,你找到了一碗湯。在你喝以前,你想知道湯的溫度。可是你沒有溫度計,幸運的是,你可使用超級計算機來告訴你湯的溫度。不幸的是,這臺超級計算機最多可能須要10秒才能得到結果。
這裏須要有幾點須要注意:
result
的全局變量。Math.random()
和setTimeout()
模擬網絡延遲的持續時間。Manth.random()
模擬溫度。運行函數並打印結果。
getTemperature();
console.log(results); // undefined
複製代碼
該功能須要必定的時間才能運行。在延遲結束以前,不會設置變量。所以,當咱們運行該函數時,setTimeout
是異步的。setTimeout
中的部分代碼移出主線程進入等待區域。
我這裏有篇文章深刻研究了這個過程:Thrown For a Loop: Understanding Loops and Timeouts in JavaScript
因爲設置變量result
的函數部分移動到了等待區域直到完成,所以咱們的解析器能夠自由移動到下一行。在咱們的例子中,它是咱們的console.log()
。此時,因爲咱們的setTimeout
未結束,result
仍未定義。
那咱們還能嘗試什麼呢?咱們能夠運行getTemperature()
,而後等待11秒(由於咱們的最大延遲是10秒),而後打印出結果。
getTemperature();
setTimeout(() => {
console.log(result);
}, 11000);
// Too Hot | Delay: 3323 | Temperature: 209 deg
複製代碼
這是可行的,但這種技術問題是,儘管在咱們的例子中,咱們知道了最大的網絡延遲,但在實際中它可能偶爾須要超過10秒。並且,即便咱們能夠保證最大延遲10秒,若是result
出結果了,咱們也是在浪費時間。
咱們將重構getTemperature()
函數以返回promise。而不是設置結果。咱們將拒絕promise,除非結果是「恰到好處」,在這種狀況下咱們將解決promise
。在任何一種狀況下,咱們都會傳遞一些值到resolve
和reject
。
如今,咱們可使用正在返回的promise結果(也稱爲消費promise)。
getTemperature()
.then(result => console.log(result))
.catch(error => console.log(error));
// Reject: Too Cold | Delay: 7880 | Temperature: 43 deg
複製代碼
.then
,當咱們的promise解決時,它將被調用,並返回咱們傳遞給resolve
的任何信息。
.catch
,當咱們的promise拒絕時,它將被調用,並返回咱們傳遞給reject
的任何信息。
最有可能的是,你將更多的使用promise,而不是建立它們。在任何狀況下,它們有助於使咱們的代碼更優雅,可讀和高效。
return new Promise((resolve, reject)=> {})
返回一個promise。.then
從已經解決的promise中獲取信息,而後使用.catch
從拒絕的promise中獲取信息。