您可以将回调转换为承诺

2025-06-07

您可以将回调转换为承诺

回调函数从一开始就是 JavaScript 的一部分,需要明确的是,我认为回调函数本身并没有什么问题。它们有其用途,而且表现非常出色。我仍然经常使用回调函数。

我甚至发布了一些视频,讲解了什么是回调函数以及如何在代码中使用它们。以下是一些示例:

一些开发人员在使用回调时遇到的问题被称为callback hell。当你将多个回调嵌套在一起时,就会发生这种情况。

这是一个完全虚构的例子,可以让您了解我的意思。

myObject.someTask((returnObj) => {
  //this is the success callback
  //our `returnObj` is an object that also has a method
  //which uses a callback

  returnObj.otherTask( (otherObj) => {
    //successfully ran `otherTask`
    //the `otherObj` sent back to us 
    // has a method with callbacks

    otherObj.yetAnotherTask( (anotherObj) => {
      //success running yetAnotherTask
      // we are reaching callback hell
      // imagine if anotherObj had a method 
      // which used callbacks...
    },
    (error)=>{
      //failed to run yetAnotherTask
    }
  },
  (error)=>{
    //failed to run otherTask
  }); //end of otherTask
},
(error)=>{
  //this is the error callback
}); //end of someTask 
Enter fullscreen mode Exit fullscreen mode

上述代码的目标是运行myObject.someTask( )。运行完成后,我们希望运行returnObj.otherTask( ),并使用 返回的对象someTaskotherTask运行完成后,我们希望调用otherObj.yetAnotherTask( )

我确信你已经明白我的意思了。

只是因为我们想按顺序运行这三种方法,所以我们最终创建了这一大组嵌套的花括号和函数调用。

代码运行正常,没有错误。但是嵌套的括号和花括号很容易出现拼写错误,并且难以阅读。

承诺的不同之处

我们Promises可以将一系列任务转换成更易于阅读的内容。每个任务都有自己的then( )方法作为包装器,我们可以将它们链接在一起。

Promise.resolve()
  .then(()=>{
    //first task
  })
  .then((returnedValue)=>{
    //second task
  })
  .then((returnedValue)=>{
    //third task
  })
  .catch((error)=>{
    //handle errors from any step
  })
Enter fullscreen mode Exit fullscreen mode

包装回调

现在,虽然我们不能采用内置函数navigator.geolocation.getCurrentPosition( )并更改本机代码以将其转换为Promise,但我们可以将其包装在其中以创建我们在所有项目中使用的实用函数。

基本 Promise 语法

当我们创建一个 Promise 时,我们使用new运算符并提供一个具有两个参数的函数:一个在解决承诺时调用;一个在拒绝承诺时调用。

let p = new Promise( (resolve, reject) => {
  //This function is passed to the newly created Promise.
  //if we do this:
  resolve();  
  // we are saying that the Promise worked
  //if we do this:
  reject();
  // we are saying that the Promise failed
});
Enter fullscreen mode Exit fullscreen mode

插入回调函数

我们现在需要将原始回调函数放置在 Promise 内的 resolve-reject 函数中。

let p = new Promise( (resolve, reject) => {
  navigator.geolocation.getCurrentPosition(
        (position) => {
          //success
          resolve(position);
        },
        (err) => {
          //failed
          reject(err);
        });
});
Enter fullscreen mode Exit fullscreen mode

地理位置调用的结果现在是Promise变量中的一个对象p。我们可以在它的末尾链接then()和方法,如下所示:catch()

p.then( (position)=>{
  console.log(position.coords.latitude, position.coords.longitude)
})
.catch( (err)=>{
  console.log(err); //the error from the geolocation call
})
Enter fullscreen mode Exit fullscreen mode

我们现在有一个功能性解决方案,在顶层,它使用承诺而不是回调。

然而,我们并没有对选项对象做任何事情,而且我们也没有真正做出一些在未来的项目中可以使用的东西。

可重用上下文

为了能够重用我们很酷的位置 Promise 而不重复自己,我们应该将此代码包装在一个函数中。

该功能还应包括对浏览器对地理位置的支持测试。

const getLocation = () => {
  //check for browser support first
  if('geolocation' in navigator){
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          //success
          resolve(position);
        },
        (err) => {
          //failed
          reject( err );
        }
      );
    });
  }else{
    let err = new Error('No browser support for geolocation');
    return Promise.reject(err);
  }
}
Enter fullscreen mode Exit fullscreen mode

如果浏览器缺乏对地理位置的支持,那么我们应该返回一个包含错误对象的失败承诺。

现在,我们可以调用 getLocation 函数并在其上链接thencatch方法。

getLocation( )
  .then( pos => {
    //success. We have a position Object
  })
  .catch( err => {
    console.log(err); //the error from the geolocation call
  });
Enter fullscreen mode Exit fullscreen mode

添加对参数的支持

因此,我们有一个基于 Promise 的调用,geolocation但我们仍然无法自定义调用的选项参数getCurrentPosition

我们需要能够将选项对象传递给我们的 getLocation 函数,如下所示:

let options = {
  enableHighAccuracy: true,
  timeout: 10000,
  maximumAge: 0,
}
getLocation(options).then( ... ).catch( ... );
Enter fullscreen mode Exit fullscreen mode

在我们的 getLocation 函数内部,我们可以测试参数是否传入,提供一组默认值,然后将其getCurrentPosition作为第三个参数传递给方法。

const getLocation = (opts) => {
  if('geolocation' in navigator){
    opts = opts ? opts: {
          enableHighAccuracy: false,
          timeout: 10000,
          maximumAge: 0,
        };
    navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve(position); //success
        },
        (err) => {
          reject( err ); //failed
        },
        opts
      ); //opts is the third argument
    });
  }else{
    //...same as before
  }
}
Enter fullscreen mode Exit fullscreen mode

三元语句是检查是否传入参数的好方法,如果没有传入,则赋予默认值。另一种方法是使用解构函数并赋默认值。(不过那是另一篇文章了。)

把我变成一个模块

如果您已经使用 ES6 模块语法将实用程序函数(如这个)导入到您的网站和项目中,那么我们可以用这种方法做同样的事情。

将我们完成的函数声明和表达式放入名为的文件中utils.js

//utils.js

const getLocation = (opts) => {
  if ('geolocation' in navigator) {
    opts = opts ? opts : {
          enableHighAccuracy: true,
          timeout: 10000,
          maximumAge: 30000,
        };
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve(position); //success
        },
        (err) => {
          reject( err ); //failed
        },
        opts
      );
    });
  } else {
    let err = new Error('No browser support for geolocation');
    return Promise.reject(err);
  }
};

export { getLocation };
Enter fullscreen mode Exit fullscreen mode

作为该文件的最后一行,我们导出了基于 Promise 的酷炫新地理定位解决方案。

然后,回到我们网站的主 JavaScript 文件中,我们导入代码以便可以使用它。

//main.js
import { getLocation } from './util.js';

document.body.addEventListener('click', (ev)=>{
  //click the page to get the current location
  let options = {
    enableHighAccuracy: true,
    timeout: 15000,
    maximumAge: 0,
  };
  getLocation(options)
    .then((pos) => {
      //got the position
      console.log('Latitude', pos.coords.latitude);
    })
    .catch((err) => {
      //failed
      console.warn('Reason:', err.message);
    });
});
Enter fullscreen mode Exit fullscreen mode

这就是全部了。现在,我们得到了一段之前只用于回调的代码,我们让它以基于 Promise 的方法的形式运行。

您可以使用任何回调方法遵循这种方法并构建自己的基于承诺的实用程序函数库。

请记住,Chrome 现在需要 HTTPS 来测试地理定位功能。如果您通过 localhost 测试此代码,Firefox 仍然允许您在不使用 HTTPS 的情况下运行它。

如果您想了解有关 Promises、Javascript 或几乎任何 Web 开发主题的更多信息:请查看我的YouTube 频道,其中有数百个视频教程。

文章来源:https://dev.to/prof3ssorst3v3/you-can-convert-your-callbacks-to-promises-141n
PREV
如何免费开始使用 Elon Musk AI Grok
NEXT
使用 JavaScript 从图像中提取调色板