译者按: 使用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、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

版权声明:

转载时请注明作者Fundebug以及本文地址:

https://blog.fundebug.com/2017/09/04/promise-share-variable/

如何在Promise链中共享变量?的更多相关文章

  1. 从 axios 源码中了解到的 Promise 链与请求的取消

    axios 中一个请求取消的示例: axios 取消请求的示例代码 import React, { useState, useEffect } from "react"; impo ...

  2. 中断或取消Promise链的可行方案

    ES6标准引入的异步编程解决方案Promise,能够将层层嵌套的回调转化成扁平的Promise链式调用,优雅地解决了“回调地狱”的问题.当Promise链中抛出一个错误时,错误信息沿着链路向后传递,直 ...

  3. 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架

    一直准备写这么一篇有关 SSIS 日志系统的文章,但是发现很难一次写的很完整.因为这篇文章的内容可扩展的性太强,每多扩展一部分就意味着需要更多代码,示例和理论支撑.因此,我选择我觉得比较通用的 LOG ...

  4. 如何在NodeJS项目中优雅的使用ES6

    如何在NodeJS项目中优雅的使用ES6 NodeJs最近的版本都开始支持ES6(ES2015)的新特性了,设置已经支持了async/await这样的更高级的特性.只是在使用的时候需要在node后面加 ...

  5. 使用Promise链式调用解决多个异步回调的问题

    使用Promise链式调用解决多个异步回调的问题 比如我们平常经常遇到的一种情况: 网站中需要先获取用户名,然后再根据用户名去获取用户信息.这里获取用户名getUserName()和获取用户信息get ...

  6. 从如何停掉 Promise 链说起

    在使用Promise处理一些复杂逻辑的过程中,我们有时候会想要在发生某种错误后就停止执行Promise链后面所有的代码. 然而Promise本身并没有提供这样的功能,一个操作,要么成功,要么失败,要么 ...

  7. 手摸手教你如何在 Python 编码中做到小细节大优化

    手摸手教你如何在 Python 编码中做到小细节大优化 在列表里计数 """ 在列表里计数,使用 Python 原生函数计数要快很多,所以尽量使用原生函数来计算. &qu ...

  8. promise链式调用的应用

    then在链式调用时,会等前一个then或者函数执行完毕,返回状态,才会执行回调函数. (1)代码顺序执行,第一步调用了函数cook ,cook执行返回了一个promise,promise返回的是成功 ...

  9. 如何在 Visual Studio 中使用 Git 同步代码到 CodePlex

    开源社区不管在国内还是国外都很火热,微软也曾因为没有开源而倍受指责,但是随着 .Net framework.ASP.Net MVC等框架的逐渐开源,也让大家看到了微软开源的步伐.CodePlex 则是 ...

随机推荐

  1. W3bsafe]SQLmap过狗命令的利用+教程

    W3bsafe]SQLmap过狗命令的利用+教程 本文转自:i春秋社区 我就是那个爱装逼的小人   本屌又来装逼了 SQLmap注入的时候 有的肯定会被安全狗拦截吧 本屌来教各位过狗!过waf等安全狗 ...

  2. itemKNN发展史----推荐系统的三篇重要的论文解读

    itemKNN发展史----推荐系统的三篇重要的论文解读 本文用到的符号标识 1.Item-based CF 基本过程: 计算相似度矩阵 Cosine相似度 皮尔逊相似系数 参数聚合进行推荐 根据用户 ...

  3. 解决微信小程序的wx-charts插件tab切换时的显示会出现位置移动问题-tab切换时,图表显示错乱-实现滑动tab

    解决Echarts在微信小程序tab切换时的显示会出现位置移动问题 tab切换时,图表显示错乱 <canvas class="kcanvas" canvas-id=" ...

  4. Metasploit Framework(2)Exploit模块、Payload使用

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 Exploit模块分为主动和被动(Active.Pas ...

  5. 应用监控CAT之cat-client源码阅读(一)

    CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...

  6. feign包名路径添加问题

    1. feign包名路径添加问题 1.1. 问题 在SpringCloud中使用feign调用路径中,不能在类上直接添加@RequestMapping(value = "/hospital- ...

  7. springboot下载文件

    例子 /** * 下载假期模板 */ @ResponseBody @RequestMapping(value = "/downloadDolidayTemplate.do") pu ...

  8. mysql怎么限制ip访问

    grant all privileges on *.* to 'root'@'ip'identified by '密码'; #授权某个ip的用户可以通过密码访问数据库

  9. MyBatis别名与util类技能了解

    1.别名 在java中String类型就是String类型,但是在MyBatis中可不会识别java中的类型,在MyBatis中String类型的别名是'string',小写的String,或者也可以 ...

  10. java开发面试问题

    Java面试题:java的垮平台原理 为什么要跨平台使用????? 其实说白了就是个操作系统支持的指令集是不一样的.我们的程序需要再不同的操作系统上运行这些代码. 但是不要说jvm是跨平台的,而真正跨 ...