如何让异步接口更优雅的同时支持 callback 和 promise
发布于 8 年前 作者 Amastyer 5817 次浏览 来自 分享

看到精华里有篇文章叫做 如何让异步接口同时支持 callback 和 promise,少爷打算发表少爷在cnode的第一篇文章,无奈取名是个技术活,偏偏少爷又只有初中文凭,只好在原文标题上加了几个字,希望兄台莫怪👻

实现

我文采不好就不啰嗦了,直接上代码:

export const createPromiseCallback = () => {
  let cb;
  if (!global.Promise) {
    cb = () => {};
    cb.promise = {};

    const throwPromiseNotDefined = () => {
      throw new Error(
        'Your Node runtime does support ES6 Promises. ' +
        'Set "global.Promise" to your preferred implementation of promises.');
    };

    Object.defineProperty(cb.promise, 'then', { get: throwPromiseNotDefined });
    Object.defineProperty(cb.promise, 'catch', { get: throwPromiseNotDefined });
    return cb;
  }

  const promise = new global.Promise((resolve, reject) => {
    cb = (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    };
  });
  cb.promise = promise;
  return cb;
};

用法

function xxx(p1, p2, fn) {
  fn = fn || createPromiseCallback();
  // your code here...
  return fn.promise;
}

栗子

// define
User.createPost = (id, post, fn) => {
  fn = fn || createPromiseCallback();
  User.findById(id, (err, user) => {
    if (err) return fn(err);
    if (!user) return fn(new Error('User is not found.'));
    user.post.create(post, (err, post)=> {
      if (err) return fn(err);
      fn(null, post);
    });
  }); 
  return fn.promise;
}

// use callback
User.createPost(userId, post, (err, instance) => {
  // do something...
});

// use promise
User.createPost(userId, post).then((instance) =>{
  // do something...
}).catch((err) => {
  // handle error
});

把promise的逻辑单独拆出来,在需要兼容的函数里只要判断fn就行,是不是很优雅,少爷可不是标题党! 相信写过loopback的童鞋看着一定很熟悉,其实代码就出自loopback哦!

7 回复

在dao层把promise的逻辑单独拆出来是必须的,而且未来有很多好处,尤其是和async结合的时候,升级代码也是非常简单的

简要解析:当fn已定义,走回调流程;当fn未定义,使用createPromiseCallback来创建一个包含promise属性的结果处理函数,实际上是把外部的回调形式通过包装委托给了内部属性的Promise对象。createPromiseCallback有一大半代码是用来做容错的,如果不存在global.Promise,直接就给报个错,感觉省几行代码… <small>如果学习的不对,题主指点一下下</small>

妙哉 妙哉

@DevinXian 大致就是这样,不过global.Promise是可以替换为别的Promise库,这样即便当前runtime原生不支持Promise也不会受到影响,而且可以把createPromiseCallback封装到单个文件里,每次用的时候只是对函数的两行代码做了修改,侵入性非常低,不仅省代码,可维护性也非常高

我觉得将callback和promise混用还是不太好。在这个例子中,createPost()有时候会返回promise,有时候又不返回,会让人迷惑 From Noder

@leizongmin 他的意思应该是两者都支持,随便用哪个的意思吧。我们团队API目前统一封装成promise型

@DevinXian 感觉callback和promise同时存在的应用场景应该是在sdk之类,不管你内部使用promise还是其他别的黑科技,但是呈现给用户使用的时候还是应该是callback

回到顶部