为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

有只小跳蛙 2023-05-19 18:10:16
我有两个功能:上传文件到服务器(未知时间)在服务器上启动 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.5module.exports = ({ host, port, username, key }, cb) => {  ...  cb('done')}我知道这可以在 Promises 或异步函数的帮助下完成,但我所有的尝试都没有成功。应该如何正确完成?
查看完整描述

2 回答

?
跃然一笑

TA贡献1826条经验 获得超6个赞

有不同的方法可以做到这一点。我不会涵盖所有内容。


选项 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.all


const 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");    

});


查看完整回答
反对 回复 2023-05-19
?
慕的地8271018

TA贡献1796条经验 获得超4个赞

async让我们暂时忘掉函数。它们只是 Promises 之上的一点语法糖。如果您还没有 Promise,那么它们对您没有任何好处。

将 Promises 视为为您处理回调的包装器对象。他们真的仅此而已。当我们构造一个新的 Promise时,我们得到了特殊的resolvereject函数。然后我们可以使用其中一个或两个函数来代替传统的回调。因此,例如,如果我们想承诺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();

希望这能解决问题!


查看完整回答
反对 回复 2023-05-19
  • 2 回答
  • 0 关注
  • 138 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信