1. 程式人生 > >如何在Promise鏈中共享變數?

如何在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、金山軟體、百姓網等眾多知名使用者的認可。歡迎免費試用!

如何在Promise鏈中共享變數?

版權宣告

轉載時請註明作者Fundebug以及本文地址:
https://blog.fundebug.com/2017/09/04/promise-share-variable/