猿问

如何避免使用“回调地狱”并转向 Promises?

我有两个功能:

  1. 上传文件到服务器(未知时间)

  2. 在服务器上启动 ssh 命令(大约 0.1 秒)

为了解决这个问题,我使用了这样的回调:


const fs = require("fs");

const sftp = require("./_uploader/sftp");

const pm2 = require("./_uploader/pm2");


const credentials = {

  host: "",

  port: 123,

  username: "",

  key: "key",

};


sftp(credentials, () => {

  pm2(credentials, () => {

    console.log("done");

  });

});


传输协议


module.exports = ({ host, port, username, key }, cb) => {

  ...

  cb('done')

}

PM2.5


module.exports = ({ host, port, username, key }, cb) => {

  ...

  cb('done')

}

我知道这可以在 Promises 或异步函数的帮助下完成,但我所有的尝试都没有成功。应该如何正确完成?


有只小跳蛙
浏览 139回答 2
2回答

跃然一笑

有不同的方法可以做到这一点。我不会涵盖所有内容。选项 1:等待承诺对于此示例,您将希望在函数内运行代码async以利用关键字await。更新您的 SMTP 和 PM2 函数以返回一个 Promise。在 promise 内部将处理逻辑并resolve("done")释放 promise,以便代码可以继续。module.exports = ({ host, port, username, key }) => {  return new Promise((resolve) => {        ...        resolve("done");    }); }现在您可以更新执行代码以利用承诺:const fs = require("fs");const sftp = require("./_uploader/sftp");const pm2 = require("./_uploader/pm2");const credentials = {  host: "",  port: 123,  username: "",  key: "key",};const runApp = async () => {    await sftp(credentials);    await pm2(credentials);    console.log("done");}runApp();选项 2:连锁承诺另一种方法是通过链接 Promise。我选择不经常这样做,因为它会变成一团乱七八糟的嵌套逻辑。const fs = require("fs");const sftp = require("./_uploader/sftp");const pm2 = require("./_uploader/pm2");const credentials = {  host: "",  port: 123,  username: "",  key: "key",};sftp(credentials).then(result1 => {    pm2(credentials).then(result2 => {        console.log("done");        });});选项 3:承诺所有另一种选择是使用Promise.allconst fs = require("fs");const sftp = require("./_uploader/sftp");const pm2 = require("./_uploader/pm2");const credentials = {  host: "",  port: 123,  username: "",  key: "key",};Promise.all([    sftp(credentials),    pm2(credentials)]).then(result => {    console.log("done");    });

慕的地8271018

async让我们暂时忘掉函数。它们只是 Promises 之上的一点语法糖。如果您还没有 Promise,那么它们对您没有任何好处。将 Promises 视为为您处理回调的包装器对象。他们真的仅此而已。当我们构造一个新的 Promise时,我们得到了特殊的resolve和reject函数。然后我们可以使用其中一个或两个函数来代替传统的回调。因此,例如,如果我们想承诺setTimeout:const timeoutAsPromised = (delay) => {  return new Promise((resolve) => {    setTimeout(resolve, delay);  });};我们立即返回一个 Promises。这很重要。我们的函数现在必须返回 Promise,以便可以立即使用它。但是代替回调,我们使用resolvePromise 构造函数给我们的函数。现在我们可以这样称呼它:timeoutAsPromised(1000)  .then(() => console.log('One second has passed!'));对于您的用例,我们可以做很多相同的事情。只需获取您的功能并将它们包装在一个 promisified 版本中:const sftpAsPromised = (credentials) => {  return new Promise((resolve) => {    sftp(credentials, resolve);  });};尽管取决于编写者sftp和编写方式,但从头开始重写它以返回 Promise 而不是进行回调可能同样容易:module.exports = ({ host, port, username, key }) => {  return new Promise((resolve) => {    ...    resolve('done')  });};哎呀,如果你有很多这样的异步函数,并且它们都有相同的函数签名(它们接受一个参数,然后是一个回调),你甚至可以编写一个小的 promisify 实用程序来处理它们:const promisify = (fn) => (arg) => {  return new Promise((resolve) => {    fn(arg, resolve);  });};const pm2AsPromised = promisify(pm2);好的!现在让我们简单谈谈async / await。这些语法很可爱,但重要的是要始终记住它们只适用于 Promises。如果你有一些基于回调构建的异步函数,那么 async/await 对你来说毫无用处。值得庆幸的是,我们刚刚完成了承诺回调函数的工作。所以让我们创建一个包装async函数,然后await我们的 promisified 函数调用。您可以认为await基本上只是替换.then方法。const handlerServer = async () => {  await sftpAsPromised(credentials);  await pm2AsPromised(credentials);};handleServer();希望这能解决问题!
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答