參考
https://pouchdb.com/2015/05/1...
https://developer.mozilla.org...
http://es6.ruanyifeng.com/#do...
Promises
是一種編寫異步代碼的方法。
Promise 對象
用於表示一個異步操作的最終狀態(完成或失敗),以及該異步操作的結果值。
Promise 使用
Promise適用於這樣的場景,後面的操作必須根據前面的操作的結果做出相應的反應。 那麼後面的操作必須等前面的操作完成並且獲得前面操作的結果。
假設我們現在有三個操作doSomethingFirst,doSomethingSecond 和finalHandler。
doSomethingSecond 需要根據 doSomethingFirst 的結果做出反應
finalHandler 需要根據 doSomethingSecond 的結果做出反應
流程如下:
//resOfDoSomethingFirst 是 DoSomethingFirst的結果
doSomethingFirst
|-----------------|
doSomethingSecond(resOfDoSomethingFirst)
|------------------|
finalHandler(doSomethingSecond的結果)
|------------------|
實現這個場景需要解決以下兩個問題:
後面的操作 如何知道 前面的操作 完成了
後面的操作 如何知道 前面的操作 的執行結果是什麼
//包裝第一個操作在 doSomethingFirst 中
const doSomethingFirst = new Promise(function(resolve, reject) {
// ... some code
if (/*操作成功 */){
resolve(doSomethingFirstValue); //將doSomethingFirst對象的狀態從“pending”變爲“resolved”
} else {
reject(doSomethingFirstError);
}
});
//包裝第二個操作在
doSdoSomethingSecond(resOfDoSomethingFirst) {
// ... some code
return somePromise(); //返回一個promise 對象
});
//整個操作流程如下:
doSomethingFirst()
.then(doSdoSomethingSecond)
.then(finalHandler)
.catch(function (err) {
console.log(err);
})
.finally(() => {···});
通過new Promise創建的Promise 實例doSomethingFirst 有以下方法:
Promise.all(iterable)
Promise.race(iterable)
Promise.reject(reason)
Promise.resolve(value)
Promise.prototype.catch(onRejected)
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.finally(onFinally)
1. doSomethingSecond 如何知道 doSomethingFirst 操作完成了
通過doSomethingFirst狀態的變更通知。
一個 Promise有以下幾種狀態:
pending: 操作未完成。
fulfilled: 操作完成,並且成功。
rejected: 操作完成,但是失敗。
resolve()函數:
在異步操作成功時調用
作用是:將Promise對象的狀態從 pending 變爲 fulfilled,並將異步操作的結果,作爲參數傳遞出去;
reject()函數:
在異步操作失敗時調用
作用是:將Promise對象的狀態從 pending 變爲 rejected, 並將異步操作的錯誤,作爲參數傳遞出去。
當操作完成時(Promise對象的狀態變爲fulfilled 或 rejected時),doSomethingFirst 就會通過then()函數調用doSomethingSecond,doSomethingSecond就知道doSomethingFirst已經完成了。
2. doSomethingSecond 如何知道 doSomethingFirst 的執行結果是什麼
doSomethingFirst通過給then()函數調用doSomethingSecond(resOfDoSomethingFirst)並把執行結果resOfDoSomethingFirst作爲參數傳遞給doSomethingSecond
Promise.prototype.then(onFulfilled, onRejected)
用於爲 Promise 實例添加狀態改變時的回調函數
返回值:一個新的 Promise,所可以採用鏈式寫法,then()函數後面再調用then()函數
參數:
onFulfilled
是一個函數,有一個參數,用來記錄變成fulfilled狀態返回的結果
當doSomethingFirst這個Promise的狀態變成fulfilled 時,onFulfilled作爲回調函數被調用
onRejected
是一個函數,有一個參數,用來記錄變成rejected狀態返回的原因
當doSomethingFirst這個Promise的狀態變成rejected 時,onRejected作爲回調函數被調用
在當前的例子裏參數onFulfilled就是doSomethingSecond(resOfDoSomethingFirst),resOfDoSomethingFirst 記錄了doSomethingFirst 變成fulfilled狀態返回的結果。
注意事項:
當給then()傳入的參數不是函數時,它實際上將其解釋爲then(null),將使先前的Promise的結果落空
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
console.log(result); // foo
});
等價於
Promise.resolve('foo').then(null).then(function (result) {
console.log(result);// foo
});
上面的代碼並沒有按照我們期望的打印"bar", 而是打印"foo"
正確的寫法是:
Promise.resolve('foo').then(function () {
return Promise.resolve('bar');
}).then(function (result) {
console.log(result); // bar
});
給then()傳入的參數是函數時,函數的內部我們可以做以下三件事:
1. 返回另外一個promise
2. 返回一個同步值
3. 拋出一個同步錯誤
示例:返回另外一個promise
doSomethingFirst()
.then(doSdoSomethingSecond)
.then(finalHandler)
//somePromise() 返回promise對象
function doSomethingSecond(resOfDoSomethingFirst) {
return somePromise(); //有return, finalHandler接受到的是resOfDoSomethingSecond
//somePromise();// 沒有return, finalHandler接受到的是undefined
}
function finalHandler(resOfDoSomethingSecond) {
// handle resOfDoSomethingSecond
}
示例:返回同步值 && 拋出一個同步錯誤
返回同步值實際上是將同步代碼轉換爲Promisey代碼的一種很棒的方法。例如,假設我們有一個用戶的內存緩存。我們可以做到:
//getUserByName 和 getUserAccountById 都返回promise對象
getUserByName('nolan').then(function (user) {
if (user.isLoggedOut()) { //如果用戶註銷
throw new Error('user logged out!'); // 拋出一個同步錯誤
}
if (inMemoryCache[user.id]) {
return inMemoryCache[user.id]; // 返回一個同步值!
}
return getUserAccountById(user.id); // 返回一個promise!
}).then(function (userAccount) {
// I got a user account!
}).catch(function (err) {
// Boo, I got an error!
});
如果用戶註銷,catch()將收到一個同步錯誤;通過callbacks,這個錯誤會被忽略
如果任何promise被拒絕,catch()將收到一個異步錯誤。
通常情況下,一個promise 依賴於另一個promise ,但當我們需要兩個promises的輸出。我們該怎麼做
getUserByName('nolan').then(function (user) {
return getUserAccountById(user.id);
}).then(function (userAccount) {
// 在這裏我們已經獲取到了用戶賬號userAccount,
// 但是我們也需要user對象時該怎麼做?
});
解決方案:
function onGetUserAndUserAccount(user, userAccount) {
return doSomething(user, userAccount);
}
function onGetUser(user) {
return getUserAccountById(user.id).then(function (userAccount) {
return onGetUserAndUserAccount(user, userAccount);
});
}
getUserByName('nolan')
.then(onGetUser)
.then(function () {
// at this point, doSomething() is done, and we are back to indentation 0
});
Promise.prototype.catch(onRejected)
用於指定發生錯誤時的回調函數。catch 可以捕獲
返回值:返回一個Promise,處理狀態變爲rejected的情況
參數:
onRejected
是一個函數,有一個參數,用來記錄變成rejected狀態返回的原因。
當promise 狀態變爲rejected時被調用。
注意事項:
catch(rejectHandler)
等同於
.then(null, rejectHandler)或.then(undefined, rejectHandler)
但是 then(resolveHandler).catch(rejectHandler) 和then(resolveHandler, rejectHandler)
不是完全相同的。
區別在於:當使用then(resolveHandler, rejectHandler)格式時,如果resolveHandler本身拋出了錯誤,那麼rejecthandler實際上不會捕獲錯誤。所以更建議使用catch 而不是then的第二個參數。
示例:
var p1 = new Promise((resolve, reject) => {
resolve('one');
});
// catch函數中可以捕捉到resolveHandler 中的error並打印
p1.then(function () {
throw new Error('oh noes');
}).catch(function (err) {
console.log('err=', err); // Error: oh noes
});
// reject函數不能捕捉到resolveHandler中的error
p1.then(function () {
throw new Error('oh noes');
}, function (err) {
console.log('err=', err);
});
Promise.prototype.finally(onFinally)
用於指定不管 Promise 對象最後狀態如何,都會執行的操作
finally本質上是then方法的特例
promise
.finally(() => {
// 語句
});
// 等同於
promise
.then(
result => {
// 語句
return result;
},
error => {
// 語句
throw error;
}
);
返回值:返回一個Promise,這個Promise對象設置了 finally 回調函數
參數:Promise 結束後調用的函數,onFinally 這個函數不接收任何參數,它僅用於無論最終結果如何都要執行的情況。所以finally方法裏面的操作,不應依賴於 Promise 的執行結果。
示例:
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
Promise.resolve(value)
用於將現有對象轉爲 Promise 對象
new Promise(function (resolve, reject) {
resolve(someSynchronousValue);
}).then(/* ... */);
等價於
Promise.resolve(someSynchronousValue).then(/* ... */);
返回值:返回一個Promise 對象,這個Promise對象是被給定的值解析過的。
參數:
value
將被Promise對象解析的參數
參數類型:
Promise對象
具有then方法的對象
沒有then方法的對象
不帶有任何參數
示例1:參數是一個Promise對象
Promise.resolve將不做任何修改、原封不動地返回這個Promise對象
var p = Promise.resolve([1,2,3]);
console.log("p=", p); //"p=" [object Promise]
console.log('p type= ', typeof(p)); // "p type= " "object"
p.then(function(v) {
console.log("v=",v); //"v=" Array [1, 2, 3]
console.log("v type=",typeof(v)); //"v type=" "object"
});
var p2 = Promise.resolve(p);
console.log("p2=", p2); //"p2=" [object Promise]
console.log('p2 type= ', typeof(p2)); //"p2 type= " "object"
p2.then(function(v) {
console.log("p2 v=",v); //"p2 v=" Array [1, 2, 3]
console.log("p2 v type=",typeof(v)); //"p2 v type=" "object"
});
p2 == p
示例2:參數是一個具有then方法的對象
返回的promise會採用這個thenable的對象的最終狀態。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p = Promise.resolve(thenable);
p.then(function(value) {
console.log(value); // 42
});
Promise.resolve方法會將thenable對象轉爲 Promise 對象,然後就立即執行thenable對象的then方法,thenable對象的then方法執行後,對象p的狀態就變爲resolved,從而立即執行最後那個then方法指定的回調函數,輸出 42
示例3:參數是一個沒有then方法的對象
var p = Promise.resolve([1,2,3]);
console.log("p=", p); //"p=" [object Promise]
console.log('p type= ', typeof(p)); // "p type= " "object"
p.then(function(v) {
console.log("v=",v); //"v=" Array [1, 2, 3]
console.log("v type=",typeof(v)); //"v type=" "object"
});
var p = Promise.resolve(123);
console.log("p=", p); //"p=" [object Promise]
console.log('p type= ', typeof(p)); // "p type= " "object"
p.then(function(v) {
console.log("v=",v); //"v=" 123
console.log("v type=",typeof(v)); //"v type=" "number"
});
var p = Promise.resolve("123");
console.log("p=", p); //"p=" [object Promise]
console.log('p type= ', typeof(p)); // "p type= " "object"
p.then(function(v) {
console.log("v=",v); //"v=" "123"
console.log("v type=",typeof(v)); //"v type=" "string"
});
示例4:不帶有任何參數
var p = Promise.resolve();
console.log("p=", p); //"p=" [object Promise]
console.log('p type= ', typeof(p)); // "p type= " "object"
p.then(function(v) {
console.log("v=",v); //"v=" undefined
console.log("v type=",typeof(v)); //"v type=" "undefined"
});
注意事項:
立即resolve()的 Promise 對象,是在本輪“事件循環”(event loop)的結束時執行,而不是在下一輪“事件循環”的開始時
//在下一輪“事件循環”開始時執行
setTimeout(function () {
console.log('three');
}, 0);
//在本輪“事件循環”結束時執行
Promise.resolve().then(function () {
console.log('two');
});
//立即執行
console.log('one');
結果:
"one"
"two"
"three"
Promise.reject(reason)
返回值:返回一個Promise 對象,這個Promise 對象 帶有狀態是rejected的原因
參數:
reason
表示Promise被拒絕的原因
new Promise(function (resolve, reject) {
reject(someSynchronousReson);
}).then(null,function(reason){
//...
});
等價於
Promise.reject(someSynchronousReson)
.then(null, function(reason) {
//...
});
示例:
new Promise(function (resolve, reject) {
reject("reject reason");
}).then(null,function(reason){
console.log(reason);//"reject reason"
});
Promise.reject("reject reason").then(null, function(reason) {
console.log(reason); // "reject reason"
});
Promise.reject(new Error("reject reason")).then(null, function(error) {
console.log(error); // Error: reject reason
});
注意事項:與Promise.resolve不同的是Promise.reject()方法的參數,會原封不動地作爲reject的理由,變成後續方法的參數。
const thenable = {
then(resolve, reject) {
reject('出錯了');
}
};
Promise.reject(thenable)
.catch(e => { //e不是reject拋出的“出錯了”這個字符串,而是thenable對象。
console.log(e === thenable) // true
})
Promise.all(iterable)
用於將多個 Promise 實例,包裝成一個新的 Promise 實例
返回值:返回一個新的promise對象,iterable中所有的promise都變成resolved狀態時返回的 promise纔會變爲resolved狀態; iterable中有一個promise變成rejected狀態,promise就會變爲rejected狀態。
參數:
iterable
一個可迭代對象,eg Array 或 String
示例: iterable中所有的promise都變成resolved狀態時返回的 promise纔會變爲resolved狀態
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
const p = Promise.all([p1, p2, p3]).then(values => {
console.log(values); //["one", "two", "three"]
}).catch(reason => {
console.log(reason); //沒執行
});
流程:p創建時爲pending狀態,當p1, p2, p3的狀態都變成resolved時觸發p變成resolved狀態。
p1 1s
|----------|resolved
p2 2s
|--------------------|resolved
p3 3s
|------------------------------|resolved
p resolved
示例:iterable中有一個promise變成rejected狀態,promise就會變爲rejected狀態
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
reject('reject');
});
const p = Promise.all([p1, p2, p3]).then(values => {
console.log(values); //沒執行
}).catch(reason => {
console.log(reason); //"reject"
});
流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時觸發p變成rejected狀態。
p1 1s
|----------|resolved
p2 2s
|--------------------|resolved
p3
|-|rejected
p rejected
示例:如果作爲參數的 Promise 實例,自己定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()的catch方法
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
reject('reject');
}).catch(reason => {
console.log(reason); //reject
});
const p = Promise.all([p1, p2, p3]).then(values => {
console.log(values); // ["one", "two", undefined]
}).catch(reason => {
console.log(reason); //沒執行
});
流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時,調用自己定義的catch函數拋出錯誤,不會觸發p變成rejected狀態。當p1 和 p2 也執行完時,觸發p變成resolved狀態
p1 1s
|----------|resolved
p2 2s
|--------------------|resolved
p3
|-|rejected
|p resolved
示例:如果傳入的參數是一個空的可迭代對象,則返回一個resolved狀態的 Promise
const p = Promise.all([]).then(values => { //p創建時狀態就爲resolved
console.log(values); // []
});
Promise.race(iterable)
同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例
返回值:返回一個新的promise對象,一旦iterable中的某個promise變爲resolved或rejected狀態,返回的 promise就會變爲resolved或rejected狀態。誰最先執行完就返回誰的狀態
參數:
iterable
一個可迭代對象,eg Array 或 String
示例: 一旦iterable中的某個promise變爲resolved狀態,返回的 promise就會變爲resolved狀態。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
const p = Promise.race([p1, p2, p3]).then(values => {
console.log(values); //["one", "two", "three"]
}).catch(reason => {
console.log(reason); //沒執行
});
流程:p創建時爲pending狀態,p1先執行完,p1的狀態都變成resolved時觸發p變成resolved狀態。
p1 1s
|----------|resolved
p resolved
p2 2s
|--------------------|resolved
p3 3s
|------------------------------|resolved
示例:一旦iterable中的某個promise變爲rejected狀態,返回的 promise就會變爲rejected狀態。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
reject('reject');
});
const p = Promise.all([p1, p2, p3]).then(values => {
console.log(values); //沒執行
}).catch(reason => {
console.log(reason); //"reject"
});
流程:p創建時爲pending狀態,p3先執行完,p3的狀態變成rejected時觸發p變成rejected狀態。
p1 1s
|----------|resolved
p2 2s
|--------------------|resolved
p3
|-|rejected
p rejected