以下几行代码中,Promise对比回调的优势在哪里?

最近学ES6,了解到promise,可以通过then().then().then()的方式解决回调地狱
想请问:
像下边的简单逻辑情况下,两者的功效可能一样的。
那么promise还有什么优势吗?
如果有,在哪里,希望得到老司机码力加持,请不惜金码,敲上两行。
谢谢你的帮助

    function afterSomeSeconds( callBackFn ){
        setTimeout(()=>{
            callBackFn('2秒过去了,异步执行结束');
        }, 2 * 1000);
    };
    afterSomeSeconds(function toLog(msg){
        console.log(msg)
    }); 
    console.log('主线程1');

    // ▲传统回调,
    // ——————————————————————————————————————————————————————————
    // ▼ES6的Promise

    function afterSomeSecondsPromise(){
        return new Promise(function(resolve, reject){
            setTimeout(()=>{
                resolve('3秒过去了,异步执行结束');
            }, 3 * 1000);
        })
    }
    afterSomeSecondsPromise().then(msg=>{
        console.log(msg)
    });
    console.log('主线程2');
控制台打印如下:
主线程1
主线程2
2秒过去了,异步执行结束
3秒过去了,异步执行结束
阅读 6k
11 个回答

从本质来讲两者都能实现我们想要的功能,但真正投入开发,你会发现promise跟async/await搭配简直就是一把梭子。
利用promise跟async/await可以将异步操作改成‘伪同步’(实质还是异步),编码上思路更加顺溜了。

往往项目开发我们都会封装通用的请求函数
使用回调的封装就是

export  const Http= {
  get:function(url,data,sucCb,errCb){
    axios.get(url,{params:data})
    .then(function(res){
      if (res.data.code==200) {
          //这里可以写一些请求操作成功需要统一处理的代码,例如弹框显示成功
        sucCb(res.data.data)
      }else {
        //这里可以写一些请求操作失败需要统一处理的代码,例如弹框显示失败
        errCb(res.data.message);
      }
    })
    .catch(function(err){
      //这里可以写一些请求操作失败需要统一处理的代码,例如弹框显示失败
      errCb(err);
    })
  },
  post:function(...){...}, 
  put:function(...){...}, 
  delete:function(...){...}, 
}

如何调用呢

Http.get(url,data,function(res){
    //请求成功啦,做处理
},function(err){
   //请求失败啦,快做处理
})

当接口很多的时候,这种反复的操作能写到你烦。
那我们使用promise封装呢?结果会是怎么样

export  const Http= {
  get:function(url,data){
    return new Promise((resolve,reject)=>{
        axios.get(url,{params:data})
        .then(function(res){
          if (res.data.code==200) {
              //这里可以写一些请求操作成功需要统一处理的代码,例如弹框显示成功
            resolve(res.data.data)
          }else {
            //这里可以写一些请求操作失败需要统一处理的代码,例如弹框显示失败
            errCb(res.data.message);
          }
        })
        .catch(function(err){
          //这里可以写一些请求操作失败需要统一处理的代码,例如弹框显示失败
          reject(err);
        })
    })
  },
  post:function(...){...}, 
  put:function(...){...}, 
  delete:function(...){...}, 
}

如何调用呢

async getData(){
    let mydata = await Http.get(url,data);
}

一看码量减少了一半不止,而且封装了通用请求函数,可以对api做统一管理,放在一个api.js里。例如

export const Orders = {
  get:function(){ return Http.get("deliver/oil-orders"); },
  deliver:function(){ return Http.get("deliver/oil-orders",{section:'delivering'}); },
  put:function(data){ return Http.put(`deliver/oil-orders/${data.id}`,data); }
}

export const User = {
  code:function(phone){ return Http.get("sms/code",{phone:phone}); },
  login:function(data){ return Http.post("deliver/login",data); },
  get:function(){ return Http.get("deliver/user/info",null); }
}

以后每次调用接口都是简单的‘伪同步’
let user = await User.get();
let orders = await Orders.get();

是不是更加简洁了?

有啊!

1. await 加持下更爽

在支持 await 的环境下可以直接

var msg = await afterSomeSecondsPromise();
console.log(msg);

2.和其他异步操作结合,或者自身重复调用

fetch('./api')
 .then(afterSomeSecondsPromise)
 .then(msg => fetch('./api', {method: 'POST', body: msg}))
 .catch(err => console.error(err)); // .catch 可以捕获甚至 afterSomeSecondsPromise 里的异常

 afterSomeSecondsPromise()
  .then(afterSomeSecondsPromise)
  .then(afterSomeSecondsPromise);
 
// 试试回调版本?

我补充一点 错误处理也会变优雅, 用习惯后用 throw 来做流程控制会变为常态.

区别就是
Promise 更简洁可读性好 易操作 功能更强大一点 更加主流

你这个就好像在对比 jqueryvue,reactes5es6 不是说他差 只是有更好的 当然用更好的

优势最主要的就是体现在可读性上而已,你这里的例子过于简单了,所以说明不了问题,我给你从网上找了个图:
clipboard.png

这个图虽然有些夸张,但足够说明问题,就是当嵌套的层级多了,可读性变差、变量命名冲突等问题就会被放大,而且这只考虑了串行调用的情况,如果并行呢,请自动脑补。

再有一个问题就是,callback 的函数签名没有约束,意味着如果你愿意的话,想怎么传参就怎么传,虽然在 nodejs 有其约定俗成的签名,比如 err 是第一个参数,之后是 data 等等,但如果是在个人项目中的话,是否遵守这个规范显然取决于开发者本身了。

使用 Promise 的好处就是一定程度上解决了回调地狱带来的问题,注意这里是一定程度上噢,而不是完全解决,由于 Promise 本身的实现有规范,因此各种实现库除去定制化的功能以外,基本一定程度上保证了一致性,比如 .then 或者 .catch,这些 api 各种库都是一样的。

当然了,还有其他的异步解决方案,不过大同小异,基本上都是在可读性、可维护性、一致性上取舍,你见的多了、用的多了就知道了。

setTimeout会把你延迟执行的内容扔到下一个EventLoop里.
Promise会将你要执行的内容扔到当前EventLoop的最后.

上面回答的太多了,就是从使用上看起来更加清晰,ES7的async和await到后续使用会更加频繁

你描述都提到了用promise解决回调地狱,但你提供的代码只是一个简单的异步处理,根本就没形成地狱。你尝试写一个3秒后打印3然后过了2秒打印2然后再过过4秒打印4,然后看一下有什么区别。

你再思考一下,假如你的异步任务发生错误你该怎么处理?当然你用计时器是没有发生错误的可能的,你改用ajax,或是nodejs的io操作。

你的问题是不了解promise的使用场景,实际上promise就是为了让异步操作变得更优雅而存在的。

ES7的async和await到后续使用会更加频繁

你是用了setTimeout这种定时器再回调,那么在传统的单线程执行过程中,如果你需要去异步的处理一些事情而不影响主线程呢!建议多了解一下异步到底是怎么回事,而不是用定时器来描述。

推荐问题
宣传栏