专栏名称: Fundebug
Fundebug为JavaScript、微信小程序及Node.js开发团队提供专业的线上代码bug监控和智能分析服务。
目录
相关文章推荐
前端大全  ·  图解Nestjs - 适合中国宝宝的入门指导 ·  5 天前  
前端大全  ·  太荒谬了!千人公司一刀切禁用 ... ·  1 周前  
前端大全  ·  大家都该学学的埋点概念与使用 ·  1 周前  
大迁世界  ·  没人愿意使用这些数组方法 ·  6 天前  
大迁世界  ·  没人愿意使用这些数组方法 ·  6 天前  
得物技术  ·  Java性能测试利器:JMH入门与实践|得物技术 ·  6 天前  
得物技术  ·  Java性能测试利器:JMH入门与实践|得物技术 ·  6 天前  
51好读  ›  专栏  ›  Fundebug

如何在Promise链中共享变量?

Fundebug  · 公众号  · 前端  · 2017-09-04 09:38

正文

译者按: 使用Promise写过异步代码的话,会发现在Promise链中共享变量是一个非常头疼的问题,这也是Async/Await胜过Promise的一点,我们在Async/Await替代Promise的6个理由有提过,这篇博客将有更详细的介绍。

  • 原文: Passing data between Promise callbacks

  • 译者: Fundebug

为了保证可读性,本文采用意译而非直译,并且对源代码进行了大量修改。另外,本文版权归原作者所有,翻译仅用于学习。

基于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直接将问题消灭了,无疑是更好的方式!

参考

  • Promises for asynchronous programming

  • ES proposal: Promise.prototype.finally()

  • ES proposal: Promise.try()

  • Async/Await替代Promise的6个理由


我们欢迎转载,但是要求注明作者Fundebug保留原文地址并且附上Fundebug公众号的二维码。否则,我们将委托相关机构追诉法律责任。