译者按: 使用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. Javascript高级编程学习笔记(42)—— DOM(8)Attr类型

    Attr类型 我们在之前的文章中提到了,元素有一个 attributes 属性 该属性保存了一个 NamedNodeMap 集合 该集合中的元素也就是今天我们所要记叙的 attr 类型 主要就是方便我 ...

  2. 从零开始单排学设计模式「简单工厂设计模式」黑铁 III

    阅读本文大概需要 2 分钟. 本篇是设计模式系列的第二篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统.所以现在打算重写,加上距离现在也有一段时间了,也算 ...

  3. Eclipse 常用快捷键使用说明

    注释快捷键 Ctrl + / 选中代码后快速加//注释, 再次用取消//注释 Ctrl + Shift + / 选中代码后快速加/* */注释 Ctrl + Shift + \ 选中代码后快速取消/* ...

  4. 第73节:Java中的HTTPServletReauest和HTTPServletResponse

    第73节:Java中的HTTPServletReauest和HTTPServletResponse HTTP协议 客户端与服务器端通讯的一种规则. request: 请求行 请求头 请求体 respo ...

  5. 使用githubpages主题NexT的语法

    使用githubpages主题NexT的语法 NexT 前言 不知道为啥?网站总是不出现? 添加「标签」页面 title: 标签测试文章 tags: - Testing - Another Tag - ...

  6. 【Spark调优】小表join大表数据倾斜解决方案

    [使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...

  7. odoo开发笔记--开启后台日志记录

    odoo后台日志记录功能 修改启动文件odoo.conf 将参数logfile注释放开, logfile = /var/log/odoo/odoo-server.log login_message = ...

  8. Django -- 发送HTML格式的邮件

    提前在setting中设置邮箱server from django.core.mai import EmailMessage subject, from_email, to = 'xxx', 'nor ...

  9. 用canvas实现红心飘飘的动画效果

    两周前,项目里需要实现一个红心飘飘的点赞效果.抓耳挠腮了老半天,看了几篇大佬的文章,终于算是摸了个七七八八.不禁长叹一声,还是菜啊.先来看一下效果:(传送门进去点一波) 一.Bezier曲线运动轨迹 ...

  10. 我可能不懂Array.prototype.sort

    今天 fix 我们后台系统的一些 bug.系统是基于 beego 和模板开发的,各种前后端代码揉作一团,没有格式,没有 eslint,全局变量满天飞,连 js 代码都有后端的插值,读起来非常 酸爽. ...