当前位置: 首页 > 编程日记 > 正文

如何用JavaScript的回调函数做出承诺

by Adham El Banhawy

由Adham El Banhawy

如何用JavaScript的回调函数做出承诺 (How to make a Promise out of a Callback function in JavaScript)

Back-end developers run into challenges all the time while building applications or testing code. As a developer who is fairly new and getting acquainted with those challenges, I have never run into a challenge or inconvenience more frequently — or more memorable — than with callback functions.

后端开发人员在构建应用程序或测试代码时始终遇到挑战。 作为一个相当新手并熟悉这些挑战的开发人员,我从来没有遇到比回调函数更频繁(或更令人难忘)的挑战或不便

I am not going to delve too deeply into the details of callback and its pros and cons or alternatives like promises and async/await. For a more vivid explanation, you can check out this article which explains them thoroughly.

我不会深入探讨回调的细节及其优缺点或诸如promises和async / await之类的替代方法。 有关更生动的解释,您可以查看这篇文章 ,对它们进行彻底的解释。

回调地狱 (Callback Hell)

Callbacks are a useful feature of JavaScript’s that enables it make asynchronous calls. They are functions that are usually passed on as a second parameter to another function that is fetching data or doing an I/O operation that takes time to complete.

回调是JavaScript的一项有用功能,可使其进行异步调用。 它们是通常作为第二个参数传递给另一个函数的函数,该函数正在获取数据或执行需要花费时间才能完成的I / O操作。

For example, try making an API call using the request module or connecting to a MongoDB database. But what if both calls depend on each other? What if the data you’re fetching is the MongoDB URL that you need to connect to?

例如,尝试使用request进行API调用 模块或连接到MongoDB数据库。 但是,如果两个调用相互依赖怎么办? 如果您要获取的数据是您需要连接的MongoDB URL,该怎么办?

You’d have to nest these calls inside each other:

您必须将这些调用相互嵌套:

request.get(url, function(error, response, mongoUrl) {if(error) throw new Error("Error while fetching fetching data");MongoClient.connect(mongoUrl, function(error, client) {if(error) throw new Error("MongoDB connection error");console.log("Connected successfully to server");    const db = client.db("dbName");// Do some application logicclient.close();});});

Okay…so where’s the problem? Well, for one thing, the readability of the code suffers from this technique.

好吧...问题出在哪里? 好吧,一方面,这种技术使代码的可读性受到影响。

It may seem OK at first when the codebase is small. But this doesn’t scale well, especially if you go more layers deeper into the nested callbacks.

当代码库较小时,乍一看似乎还可以。 但这不能很好地扩展,特别是如果您在嵌套回调中更深入一些。

You will end up with a lot of closing brackets and curly braces that will confuse you and other developers no matter how neatly formatted your code is. There is a website called callbackhell that addresses this specific issue.

最终,您将得到大量的括弧和花括号,无论您的代码格式如何整洁,都会使您和其他开发人员感到困惑。 有一个名为callbackhell的网站可以解决此特定问题。

I hear some of you, including my naïve past self, telling me wrap it in an async function then await the callback function. This just doesn’t work.

我听到一些人,包括我过去的天真自我,告诉我将其包裹在async 函数然后await回调函数。 这就是行不通的。

If there is any code block after the the function that uses callbacks, that code block will execute and will NOT wait for the callback.

如果在使用回调的函数之后有任何代码块,则该代码块将执行且不会 等待回调。

Here’s that mistake that I did before:

这是我之前犯过的错误:

var request = require('request');// WRONGasync function(){let joke;let url = "https://api.chucknorris.io/jokes/random"await request.get(url, function(error, response, data) {if(error) throw new Error("Error while fetching fetching data");let content = JSON.parse(data);joke = content.value;});console.log(joke); // undefined};// Wrongasync function(){let joke;let url = "https://api.chucknorris.io/jokes/random"request.get(url, await function(error, response, data) {if(error) throw new Error("Error while fetching fetching data");let content = JSON.parse(data);joke = content.value;});console.log(joke); // undefined};

Some more experienced devs might say “Just use a different library that uses promises to do the same thing, like axios, or just use fetch”. Sure I can in that scenario, but that’s just running away from the problem.

一些经验更丰富的开发人员可能会说:“只需使用一个不同的库即可使用诺言来完成相同的工作,例如axios , 或仅使用提取 ” 当然可以,但是这确实可以解决问题。

Besides, this is just an example. Sometimes you can be locked into using a library that doesn’t support promises with no alternatives. Like using software development kits (SDKs) to communicate with platforms like Amazon Web Services (AWS), Twitter, or Facebook.

此外,这仅是示例。 有时您可能会被锁定为使用不支持promise且没有其他选择的库。 就像使用软件开发套件(SDK)与Amazon Web Services(AWS),Twitter或Facebook之类的平台进行通信一样。

Sometimes, even using a callback to do a very simple call with a quick I/O or CRUD operation is fine, and no other logic depends on its results. But you might be constrained by the runtime environment like in a Lambda function which would kill all process once the main thread finishes, regardless of any asynchronous calls that did not complete.

有时,即使使用回调通过快速的I / O或CRUD操作进行非常简单的调用也可以,并且其他逻辑均不取决于其结果。 但是您可能会受到Lambda函数之类的运行时环境的约束,该函数会在主线程完成后终止所有进程,而不管所有未完成的异步调用如何。

解决方案1(简单):使用Node的“ util”模块 (Solution 1 (easy): Use Node’s “util” module)

The solution is surprisingly simple. Even if you’re a little uncomfortable with the idea of promises in JavaScript, you will love how you can solve this issue using them.

解决方案非常简单。 即使您对JavaScript中的Promise想法有点不满意,您也会喜欢如何使用Promise解决此问题。

As pointed out by Erop and Robin in the comments, Nodejs version 8 and above now support turning callback functions into promises using the built-in util module.

正如Erop和Robin在评论中所指出的那样,Nodejs 8及更高版本现在支持使用内置util模块将回调函数转化为promises。

const request = require('request');const util = require('util');const url = "https://api.chucknorris.io/jokes/random";// Use the util to promisify the request methodconst getChuckNorrisFact = util.promisify(request);// Use the new method to call the API in a modern then/catch patterngetChuckNorrisFact(url).then(data => {let content = JSON.parse(data.body);console.log('Joke: ', content.value);}).catch(err => console.log('error: ', err))

The above code solves the problem neatly using the util.promisify method available from nodejs core library.

上面的代码使用util.promisify巧妙地解决了问题 可从nodejs核心库获得的方法。

All you have to do is use the callback function as an argument to util.promisify, and store it an a variable. In my case, that’s getChuckNorrisFact.Then you use that variable as a function that you can use like a promise with the .then() and the .catch() methods.

您要做的就是将回调函数用作util.promisify的参数,并将其存储为变量。 就我而言,这是getChuckNorrisFact。然后使用该变量作为一个功能,您可以使用像的。然后承诺().catch()方法。

解决方案2(涉及):将回调变成承诺 (Solution 2 (involved): Turn the Callback into a Promise)

Sometimes, using the request and util libraries is just not possible, whether it’s because of a legacy environment/code base or doing the requests from the client-side browser, you have to wrap a promise around your callback function.

有时,无论是由于旧版环境/代码库还是从客户端浏览器执行请求,都无法使用request和util库,您必须在回调函数周围包裹一个Promise。

Let’s take the Chuck Norris example above, and turn that into a promise.

让我们以上面的Chuck Norris为例,并将其变成一个承诺。

var request = require('request');
let url = "https://api.chucknorris.io/jokes/random";// A function that returns a promise to resolve into the data //fetched from the API or an error
let getChuckNorrisFact = (url) => {return new Promise((resolve, reject) => {request.get(url, function(error, response, data){if (error) reject(error);let content = JSON.parse(data);let fact = content.value;resolve(fact);})});
};getChuckNorrisFact(url).then(fact => console.log(fact) // actually outputs a string
).catch(error => console.(error)
);

In the code above, I put the callback-based request function inside a Promise wrapper Promise( (resolve, reject) => { //callback function}). This wrapper allows us to call the getChuckNorrisFact function like a promise with the .then()and .catch() methods. When the getChuckNorrisFact is called, it executes the request to the API and waits for either a resolve() or a reject() statement to execute. In the callback function, you simply pass the retrieved data into the resolve or reject methods.

在上面的代码中,我将基于回调的request函数放入Promise包装器Promise( (resolve, reject) => { //callback function}) 。 这个包装器允许我们像getChuckNorrisFact .then().catch()方法一样调用getChuckNorrisFact函数。 调用getChuckNorrisFact ,它将执行对API的请求,并等待 resolve()reject()语句执行。 在回调函数中,您只需将检索到的数据传递到resolve或reject方法。

Once the data (in this case, an awesome Chuck Norris fact) is fetched and passed to the resolver, the getChuckNorrisFact executes the then() method. This will return the result that you can use inside a function inside the then() to do your desired logic — in this case displaying it to the console.

一旦获取了数据(在这种情况下,这是一个很棒的Chuck Norris事实)并将其传递给解析器,则getChuckNorrisFact执行then()方法。 这将返回结果,您可以then()内部的函数中使用该结果来执行所需的逻辑—在这种情况下,将其显示在控制台上。

You can read more about it in the MDN Web Docs.

您可以在MDN网络文档中阅读有关此内容的更多信息。

翻译自: https://www.freecodecamp.org/news/how-to-make-a-promise-out-of-a-callback-function-in-javascript-d8ec35d1f981/

相关文章:

VMware里的linux系统里的命令行里会有bee的声音,要如何关掉

VMware里的linux系统里的命令行里会有bee的声音,要如何关掉 取消bell报警声的方法:登陆linux系统vi /etc/inputrc找到set bell-style none 将前面的#去掉,之后重启系统即可解决声音问题若不见效可以通过下面的方式解决下bell-styl…

React-Todos

最近学完React的最基本概念,闲下来的时候就自己写了一个Todo-List的小应用。这里做个简略的说明,给想好好学React的新手看。 React-Todo 学习前提 这里我用了webpackb做了babel和JSX预处理和模块打包。所以对React和一些ES2015(ES6&#xff0…

Ant Design 入门-引用自己命名的组件

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 自己创建的组件:代码 import { Table, Divider, Tag } from antd; import React, { Component } from react; export default class My_Table extends Component {render() {const columns = [{title: …

迷宫出路代码_如何在软件开发的迷宫中找到自己的出路

迷宫出路代码by Tim Kleier蒂姆克莱尔(Tim Kleier) 如何在软件开发的迷宫中找到自己的出路 (How to find your way through the corn maze of software development) The corn maze is one of my favorite challenges to tackle. It’s an unnerving experience, especially w…

打包 React 项目并在服务器运行。

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 1.找到项目根目录的package.json文件:如图: 2.打开cmd执行:npm run build 3.生成DIST文件夹。 4.把DIST文件放到服务器phpStudty根目录,访问index.html。…

一些有用的Python问题

1. 修改IDLE工作路径,在命令交互模式下输入如下指令: >>> import os >>> os.getcwd() #查看当前的工作路径 >>> os.chdir(E:\\Python\\Demo) #修改当前的工作路径 2.Python中 ImportError: cannot import name NUMPY_MKL 的…

Python核心编程笔记---- print

在仅用变量名时,输出的字符串是用单引号括起来的。这个是为了让非字符串对象也可能以字符的形式显示在屏幕上。 而print 函数打印出来的是变量的值。 print 调用的是str()方法。而仅用变量名时调用的是repr()方法。 证明:------------------------------…

latex 插图解释_大O符号-只需插图和视频即可解释

latex 插图解释Big O notation is used to communicate how fast an algorithm is. This can be important when evaluating other people’s algorithms, and when evaluating your own! In this article, I’ll explain what Big O notation is and give you a list of the m…

[YTU]_2002(C语言实验——单词统计)

Description 从键盘输入一行字符,统计其中单词的个数,各单词以空格分隔,且空格数可以是多个。Input 输入只有一行句子。仅有空格和英文字母构成。 Output 单词的个数。 Sample Input stable marriage problem Consists of Matching memb…

资本中国人物-金融

一、二、三、店、五、土地、七、八、玖、拾起、白、千、一万、一亿、元(圆)、角、支、零、整个。这是上图中我们经常要填写。问:什么是它用数词?想必很多人都不是很清楚! 请看下面的两个相关的表数词: 1、数字化和大、小写数字对照…

Ant Design Pro 网络请求流程

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 在 Ant Design Pro 中,一个完整的前端 UI 交互到服务端处理流程是这样的: UI 组件交互操作; 调用 model 的 effect; 调用统一管理的 service 请求函数&a…

在Google Cloud Platform上持续部署Node.js

by Gautam Arora由Gautam Arora 在Google Cloud Platform上持续部署Node.js (Continuous Deployment for Node.js on the Google Cloud Platform) Google Cloud Platform (GCP) provides a host of options for Node developers to easily deploy our apps. Want a managed ho…

hdu-1108 最小公倍数

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid1108 题目类型: 数论 题意概括: 求两个数的最小公倍数 解题思路: 模拟 题目: 最小公倍数 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/O…

sql-schema与catalog

schema: 指的是说当偶create database caiceclb时,caiceclb就是一个schema catalog: 指的是所有的database目录,就像上图显示的那样,将MySQL原来的(mysql,infomation_schema)及后来新建的的data…

这是如何更好地利用JavaScript数组的方法

by pacdiv由pacdiv 这是如何更好地利用JavaScript数组的方法 (Here’s how you can make better use of JavaScript arrays) Quick read, I promise. Over the last few months, I noticed that the exact same four mistakes kept coming back through the pull requests I c…

07、C语言——函数

函数 1、函数定义 函数返回值类型 函数名(形式参数列表)      {         函数体;      } 注意: 定义有参函数时,形参的定义可以采用传统方式或现代方式两种 1)传统方式: int …

This is probably not a problem with npm. There is likely additional logging output above

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 E:\weii_objct\invoice-manage-web-view>npm start > ant-design-pro@2.1.0 start E:\weii_objct\invoice-manage-web-view > cross-env APP_TYPE=site umi dev cross-env 不是内部或外部命令…

parcel react_如何使用Parcel捆绑React.js应用程序

parcel reactby Michael Ozoemena迈克尔奥索埃梅纳(Michael Ozoemena) 如何使用Parcel捆绑React.js应用程序 (How to use Parcel to bundle your React.js application) 什么是包裹? (What’s Parcel?) Parcel is a web application bundler which offers a blazi…

SparkSQL 与 Spark Core的关系

不多说,直接上干货! SparkSQL 与 Spark Core的关系 Spark SQL构建在Spark Core之上,专门用来处理结构化数据(不仅仅是SQL)。 Spark SQL在Spark Core的基础上针对结构化数据处理进行很多优化和改进, 简单来讲: Spark SQ…

linux操作系统-设置静态ip

在使用linux虚拟机的时候因为经常有关机的需求,然后重新开机后可能面临这上一次获取的ip被改变,在这里我分享一下在linux 下设置静态ip的经验 1.查看路由状态 [rootlocalhost ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Met…

判断数组里面的下标是否等于一个字符串

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 使用includes() 1、判断字符串里面是否包含一个字符串 示例: var a aaaaaaaavconsole.log(a.includes(v)); //truevar a aaaaaaaavconsole.log(a.includes(c)); //falsevar a aa…

如何获得更多的自由开发者客户

by Jad Joubran通过贾德乔布兰(Jad Joubran) 如何获得更多的自由开发者客户 (How to get more clients as a freelance developer) 我希望几年前知道的实用技巧 (Practical tips I wish I knew a few years ago) Whenever a conversation about freelancing kicks off with fe…

2017.6.4 入门组 NO.2——睡眠

其实这题就是将第二个时间-第一个时间,小于0的补全就A了代码如下: var x,y,k:string;l1,l2,x1,x2,x3,y1,y2,y3:longint; beginreadln(x);readln(y);l1:pos(:,x);l2:pos(:,y);k:copy(x,1,2); val(k,x1);k:copy(x,l11,2); val(k,y1);k:copy(y,1,2); val(k…

微信小程序获取用户收货地址 完整代码

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 获取用户收货地址需要用户点击授权,所以有两种情况,确认授权、取消授权。 情况一,用户第一次访问用户地址授权,并且点击确定授权。 情况二,用…

easyui的combobox下拉框初始化默认值以及保持该值一直显示的方法

easyui的combobox下拉框默认初始值是空,下面是实现从远程加载数据之后初始化默认值,以及让该值一直排在下拉框的最顶部的方式。 目前的需求是需要在初始化的时候添加"全部数据库"字段,并且在下拉的时候,"全部数据库"一直排在最顶部。 初始化效果…

关系数据库非关系数据库_如何与关系数据库最佳配合

关系数据库非关系数据库Relational databases handle data smoothly, whether working with small volumes or processing millions of rows. We will be looking at how we can use relational databases according to our needs, and get the most out of them.关系数据库无论…

看过的bootstrap书籍(附下载地址)

http://yun.baidu.com/share/link?shareid3820784617&uk1008683945 以下书籍下载地址。 《BootStrap入门教程》 就是网上的常规教程,过了一遍,不是很重要。 《Bootstrap实战_第一章》 没找到其余的章节,不过这本书不如直接看网上的boots…

Sql的连接表补充

连接条件可在FROM或WHERE子句中指定&#xff0c;建议在FROM子句中指定连接条件。WHERE和HAVING子句也可以包含搜索条件&#xff0c;以进一步筛选连接条件所选的行。 连接可分为以下几类&#xff1a; 内连接。&#xff08;典型的连接运算&#xff0c;使用像 或 <…

js正则验证身份证号是否正确

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 封装js公共方法 //验证身份证格式 const IdentityCodeValid sId > {const aCity { 11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古…

jupyter笔记本_如何为Jupyter笔记本电脑设置PySpark

jupyter笔记本by Tirthajyoti Sarkar由Tirthajyoti Sarkar 如何为Jupyter笔记本电脑设置PySpark (How to set up PySpark for your Jupyter notebook) Apache Spark is one of the hottest frameworks in data science. It realizes the potential of bringing together both …