譯文地址:2ality.com/2017/08/pro…
譯者:bryan.yang,美團金融FEhtml
在基於Promise的代碼中,會有不少的回調函數,每一個回調函數都有本身的變量做用域。若是你想在這些回調函數之間共享數據該怎麼辦呢?如下這篇文章將會給出幾種幾種解決方法。數據庫
如下面這段Promise回調代碼中常見的問題爲例:在第一個Promise回調中(line A)聲明瞭變量connection,而這個變量將會在接下來的兩個回調中(line B and line C)引用。數組
db.open()
.then(connection => { // (A)
return connection.select({ name: 'Jane' });
})
.then(result => {
// 獲取結果
// 用 `connection` 進行更多的查詢操做 (B)
})
···
.catch(error => {
// 捕獲錯誤
})
.finally(() => {
connection.close(); // (C)
});複製代碼
在代碼中咱們使用了Promise.prototype.finally(),它是ES新添加的一個特性,工做原理與try語句塊中的finally相似。promise
第一種解決方式就是直接將共享的數據存儲在外層的做用域的變量中(line A),而後在每一個回調中使用。bash
let connection; // (A)
db.open()
.then(conn => {
connection = conn;
return connection.select({ name: 'Jane' });
})
.then(result => {
// 獲取結果
// 使用 `connection` 執行更多的查詢操做 (B)
})
···
.catch(error => {
// 捕獲錯誤
})
.finally(() => {
connection.close(); // (C)
});複製代碼
不難看出,因爲connection變量位於外層域中,因此在B與C的回調中都可以使用這個變量。異步
在介紹這個方式以前咱們先來看一下以同步的方式實現的代碼:async
try {
const connection = await db.open();
const result = await connection.select({ name: 'Jane' });
···
} catch (error) {
// 捕獲錯誤
} finally {
connection.close();
}複製代碼
在同步的方式中,咱們要想共用connection這個數據,一般也是在外層做用域中聲明一個變量來存儲,代碼以下:函數
const connection = await db.open();// (A)
try {
const result = await connection.select({ name: 'Jane' });
···
} catch (error) {
// 捕獲錯誤
} finally {
connection.close();
}複製代碼
其實咱們可使用Promise實現相似的效果 ---- 嵌套Promise,代碼以下:post
db.open() // (A)
.then(connection => { // (B)
return connection.select({ name: 'Jane' }) // (C)
.then(result => {
// 獲取結果
// 使用 `connection` 執行更多查詢操做
})
···
.catch(error => {
// 捕獲錯誤
})
.finally(() => {
connection.close();
});
})複製代碼
在上面這段代碼中有兩個Promise:ui
可能你已經注意到,在同步和嵌套Promise的實現方式中,db.open()這個同步方法拋出的錯誤並不會被捕獲。這篇文章A follow-up blog post on Promise.try()將會告訴你如何來完善這段異步代碼。在同步的代碼中你能夠將db.open()使用try語句來捕獲相關異常。
下面的這些代碼將會演示多個回調之間傳遞數據的另外一種方法。固然,這些代碼並不老是可用的。實際上,你並不能用這段代碼來鏈接你正在運行的數據庫。咱們先來看一個可以運行的栗子。
正如咱們正在解決的問題同樣:在這段代碼中,位於位置A的Promise中的intermediate變量須要在位置B中引用。
return asyncFunc1()
.then(result1 => { // (A)
const intermediate = ···;
return asyncFunc2();
})
.then(result2 => { // (B)
console.log(intermediate);
···
});複製代碼
咱們能夠經過在第一個回調中使用Promise.all()傳遞多個值給第二個回調的方式來解決這個問題:
return asyncFunc1()
.then(result1 => {
const intermediate = ···;
return Promise.all([asyncFunc2(), intermediate]); // (A)
})
.then(([result2, intermediate]) => {
console.log(intermediate);
···
})複製代碼
須要注意的是在第一個回調中直接返回一個數組並不能生效,由於若是直接返回一個數組,then()回調將會接收到一個由Promise和一個普通值組成的數組,這並非咱們想要的結果。這裏使用Promise.all(),它將會使用Promise.resolve()方法將數組中的每個元素轉換成Promise,而後執行轉換以後的Promise,並將結果放到一個普通數組中(若是每一個Promise執行成功)。這個方法也有是侷限的,你不能把你想要共享的數據傳遞到catch和finally語句塊中。 最後這種方法將會從Promise.all()支持Object參數的版本受益。而後你將能夠標記每個返回值了。