如何在Promise鏈中共享變量?


譯者按: 使用Promise寫過異步代碼的話,會發現在Promise鏈中共享變量是一個非常頭疼的問題,這也是Async/Await勝過Promise的一點,我們在Async/Await替代Promise的6個理由有提過,這篇博客將有更詳細的介紹。

為了保證可讀性,本文采用意譯而非直譯,並且對源代碼進行了大量修改。另外,本文版權歸原作者所有,翻譯僅用於學習。

基於Promise編寫異步代碼時,通常會使用多個then組成鏈式調用,每一個then都會有一個回調函數。因此,在Promise鏈中,會有很多回調函數,每個回調函數都有一個獨立的變量作用域。那么,如何在這些回調函數之間共享變量呢?這篇博客將探討這個問題。

問題

connection變量在A處定義,在BC處都需要使用。但是,由於A、B、C處於各自獨立的作用域,connection變量將不能在BC處直接使用。

db.open()
.then( connection => // A
{
return connection.select(
{
name: 'Fundebug'
});
})
.then( result =>
{
connection.query(); // B
})
.catch( error =>
{
// ...
})
.finally( () =>
{
connection.close(); // C
});

方法1:使用高階作用域變量

在更高階的作用域定義connection變量,在D處賦值,這樣在BC處直接使用了。

let connection; // A
db.open()
.then( conn =>
{
connection = conn; // D
return connection.select(
{
name: 'Fundebug'
});
})
.then( result =>
{
connection.query(); // B
})
.catch( error =>
{
// ...
})
.finally( () =>
{
connection.close(); // C
});

問題:如果需要共享的變量過多(這是很常見的情況),則需要在高階作用域中定義很多變量,這樣非常麻煩,代碼也比較冗余。

方法2:嵌套作用域

將需要使用connection變量的Promise鏈內嵌到對應then回調函數中,這樣在BC處直接使用了。

db.open()
.then( connection => // A
{
return connection.select(
{
name: 'Fundebug'
})
.then( result =>
{
connection.query(); // B
})
.catch( error =>
{
// ...
})
.finally( () =>
{
connection.close(); // C
});
});

問題:之所以使用Promise,就是為了避免回調地域,將多層嵌套的回調函數轉化為鏈式的then調用;如果為了共享變量采用嵌套寫法,則要Promise有何用?

方法3:return多個值

intermediate變量在A處定義並賦值,而在B處需要使用;但是,由於AB處於不同的作用域,B出並不能直接使用intermediate變量:

return asyncFunc1()
.then( result1 =>
{
const intermediate = ··· ; // A
return asyncFunc2();
})
.then( result2 =>
{
console.log(intermediate); // B
});

A處使用Promise.all返回多個值,就可以將intermediate變量的值傳遞到B處:

return asyncFunc1()
.then( result1 =>
{
const intermediate = ···;
return Promise.all([asyncFunc2(), intermediate]); // A
})
.then( ([result2, intermediate]) =>
{
console.log(intermediate); // B
});

問題: 使用Promise.all用於傳遞共享變量,看似巧妙,但是有點大材小用,並不合理;不能將變量傳遞到.catch()finally()中;當共享變量過多,或者需要跨過數個.then(),需要return的值會很多。

方法4: 使用Async/Await

Async/Await是寫異步代碼的新方式,可以替代Promise,它使得異步代碼看起來像同步代碼,可以將多個異步操作寫在同一個作用域中,這樣就不存在傳遞共享變量的問題了!!!

方法1中的示例可以改寫為:

try
{
var connection = await db.open(); // A
const result = await connection.select(
{
name: 'Fundebug'
});
connection.query(); // B
}
catch (error)
{
// ...
}
finally
{
connection.close(); // C
}

方法3中的示例可以改寫為:

try
{
result1 = await asyncFunc1();
const intermediate = ··· ;
result2 = await asyncFunc2();
console.log(intermediate);
}
catch (error)
{
// ...
}

毋庸贅言,Async/Await直接將問題消滅了,無疑是更好的方式!

參考


關於Fundebug :

Fundebug專注於JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了7億+錯誤事件,得到了Google、360、金山軟件、百姓網等眾多知名用戶的認可。歡迎免費試用!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM