乐趣区

关于javascript:把-Nodejs-中的回调转换为-Promise

介绍

在几年前,回调是 JavaScript 中实现执行异步代码的惟一办法。回调自身简直没有什么问题,最值得注意的是“回调天堂”。

在 ES6 中引入了 Promise 作为这些问题的解决方案。最初通过引入 async/await 关键字来提供更好的体验并进步了可读性。

即便有了新的办法,然而依然有许多应用回调的原生模块和库。在本文中,咱们将探讨如何将 JavaScript 回调转换为 Promise。ES6 的常识将会派上用场,因为咱们将会应用 开展操作符之类的性能来简化要做的事件。

什么是回调

回调是一个函数参数,恰好是一个函数自身。尽管咱们能够创立任何函数来承受另一个函数,但回调次要用于异步操作。

JavaScript 是一种解释性语言,一次只能解决一行代码。有些工作可能须要很长时间能力实现,例如下载或读取大文件等。JavaScript 将这些运行工夫很长的工作转移到浏览器或 Node.js 环境中的其余过程中。这样它就不会阻止其余代码的执行。

通常异步函数会承受回调函数,所以实现之后能够解决其数据。

举个例子,咱们将编写一个回调函数,这个函数会在程序胜利从硬盘读取文件之后执行。

所以须要筹备一个名为 sample.txt 的文本文件,其中蕴含以下内容:

Hello world from sample.txt

而后写一个简略的 Node.js 脚本来读取文件:

const fs = require('fs');

fs.readFile('./sample.txt', 'utf-8', (err, data) => {if (err) {
        // 处理错误
        console.error(err);
          return;
    }
    console.log(data);
});

for (let i = 0; i < 10; i++) {console.log(i);
}

运行代码后将会输入:

0
...
8
9
Hello world from sample.txt

如果这段代码,应该在执行回调之前看到 0..9 被输入到控制台。这是因为 JavaScript 的异步管理机制。在读取文件结束之后,输入文件内容的回调才被调用。

顺便阐明一下,回调也能够在同步办法中应用。例如 Array.sort() 会承受一个回调函数,这个函数容许你自定义元素的排序形式。

承受回调的函数被称为“高阶函数”。

当初咱们有了一个更好的回调办法。那么们持续看看什么是 Promise。

什么是 Promise

在 ECMAScript 2015(ES6)中引入了 Promise,用来改善在异步编程方面的体验。顾名思义,JavaScript 对象最终将返回的“值”或“谬误”应该是一个 Promise。

一个 Promise 有 3 个状态:

  • Pending(待处理):用来批示异步操作尚未实现的初始状态。
  • Fulfilled(已实现):示意异步操作已胜利实现。
  • Rejected(回绝):示意异步操作失败。

大多数 Promise 最终看起来像这样:

someAsynchronousFunction()
    .then(data => {
        // promise 被实现
        console.log(data);
    })
    .catch(err => {
        // promise 被回绝
        console.error(err);
    });

Promise 在古代 JavaScript 中十分重要,因为它们与 ECMAScript 2016 中引入的 async/await 关键字一起应用。应用 async / await 就不须要再用回调或 then()catch() 来编写异步代码。

如果要改写后面的例子,应该是这样:

try {const data = await someAsynchronousFunction();
} catch(err) {
    // promise 被回绝
    console.error(err);
}

这看起来很像“个别的”同步 JavaScript。大多数风行的 JavaScript 库和新我的项目都把 Promises 与 async/await 关键字放在一起用。

然而,如果你要更新现有的库或遇到旧的代码,则可能会对将基于回调的 API 迁徙到基于 Promise 的 API 感兴趣,这样能够改善你的开发体验。

来看一下将回调转换为 Promise 的几种办法。

将回调转换为 Promise

Node.js Promise

大多数在 Node.js 中承受回调的异步函数(例如 fs 模块)有规范的实现形式:把回调作为最初一个参数传递。

例如这是在不指定文本编码的状况下用 fs.readFile() 读取文件的办法:

fs.readFile('./sample.txt', (err, data) => {if (err) {console.error(err);
          return;
    }
    console.log(data);
});

留神 :如果你指定 utf-8 作为编码,那么失去的输入是一个字符串。如果不指定失去的输入是 Buffer

另外传给这个函数的回调应承受 Error,因为它是第一个参数。之后能够有任意数量的输入。

如果你须要转换为 Promise 的函数遵循这些规定,那么能够用 util.promisify,这是一个原生 Node.js 模块,其中蕴含对 Promise 的回调。

首先导入ʻutil` 模块:

const util = require('util');

而后用 promisify 办法将其转换为 Promise:

const fs = require('fs');
const readFile = util.promisify(fs.readFile);

当初,把新创建的函数用作 promise:

readFile('./sample.txt', 'utf-8')
    .then(data => {console.log(data);
    })
    .catch(err => {console.log(err);
    });

另外也能够用上面这个示例中给出的 async/await 关键字:

const fs = require('fs');
const util = require('util');

const readFile = util.promisify(fs.readFile);

(async () => {
    try {const content = await readFile('./sample.txt', 'utf-8');
        console.log(content);
    } catch (err) {console.error(err);
    }
})();

你只能在用 async 创立的函数中应用 await 关键字,这也是为什么要应用函数包装器的起因。函数包装器也被称为立刻调用的函数表达式。

如果你的回调不遵循这个特定规范也不必放心。util.promisify() 函数可让你自定义转换是如何产生的。

留神 :Promise 在被引入后不久就开始风行了。Node.js 曾经将大部分外围函数从回调转换成了基于 Promise 的 API。

如果须要用 Promise 解决文件,能够用 Node.js 附带的库(https://nodejs.org/docs/lates…)。

当初你曾经理解了如何将 Node.js 规范款式回调隐含到 Promise 中。从 Node.js 8 开始,这个模块仅在 Node.js 上可用。如果你用的是浏览器或晚期版本版本的 Node,则最好创立本人的基于 Promise 的函数版本。

创立你本人的 Promise

让咱们讨论一下怎么把回调转为 util.promisify() 函数的 promise。

思路是创立一个新的蕴含回调函数的 Promise 对象。如果回调函数返回谬误,就回绝带有该谬误的 Promise。如果回调函数返回非谬误输入,就解决并输入 Promise。

先把回调转换为一个承受固定参数的函数的 promise 开始:

const fs = require('fs');

const readFile = (fileName, encoding) => {return new Promise((resolve, reject) => {fs.readFile(fileName, encoding, (err, data) => {if (err) {return reject(err);
            }

            resolve(data);
        });
    });
}

readFile('./sample.txt')
    .then(data => {console.log(data);
    })
    .catch(err => {console.log(err);
    });

新函数 readFile() 承受了用来读取 fs.readFile() 文件的两个参数。而后创立一个新的 Promise 对象,该对象包装了该函数,并承受回调,在本例中为 fs.readFile()

reject Promise 而不是返回谬误。所以代码中没有立刻把数据输入,而是先 resolve 了 Promise。而后像以前一样应用基于 Promise 的 readFile() 函数。

接下来看看承受动静数量参数的函数:

const getMaxCustom = (callback, ...args) => {
    let max = -Infinity;

    for (let i of args) {if (i > max) {max = i;}
    }

    callback(max);
}

getMaxCustom((max) => {console.log('Max is' + max) }, 10, 2, 23, 1, 111, 20);

第一个参数是 callback 参数,这使它在承受回调的函数中有点不同凡响。

转换为 promise 的形式和上一个例子一样。创立一个新的 Promise 对象,这个对象包装应用回调的函数。如果遇到谬误,就 reject,当后果呈现时将会 resolve

咱们的 promise 版本如下:

const getMaxPromise = (...args) => {return new Promise((resolve) => {getMaxCustom((max) => {resolve(max);
        }, ...args);
    });
}

getMaxCustom(10, 2, 23, 1, 111, 20)
    .then(max => console.log(max));

在创立 promise 时,不论函数是以非标准形式还是带有许多参数应用回调都无关紧要。咱们能够齐全管制它的实现形式,并且原理是一样的。

总结

只管当初回调已成为 JavaScript 中利用异步代码的默认办法,但 Promise 是一种更古代的办法,它更容易应用。如果遇到了应用回调的代码库,那么当初就能够把它转换为 Promise。

在本文中,咱们首先学到了如何 在 Node.js 中应用 utils.promisfy() 办法将承受回调的函数转换为 Promise。而后,理解了如何创立本人的 Promise 对象,并在对象中包装了无需应用内部库即可承受回调的函数。这样许多旧 JavaScript 代码能够轻松地与古代的代码库和混合在一起。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章

欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
退出移动版