关于 Promise 的 9 个提示

正如同事所说的那样,Promise 在工作中表现优异。

这篇文章会给你一些如何改善与 Promise 之间关系的建议。

1. 你可以在 .then 里面 return 一个 Promise

让我来说明这最重要的一点

是的!你可以在 .then 里面 return 一个 Promise

而且,return 的这个 Promise 将在下一个 .then 中自动解析。

.then(r => {
    return serverStatusPromise(r); // 返回 { statusCode: 200 } 的 Promise
})
.then(resp => {
    console.log(resp.statusCode); // 200; 注意自动解析的 promise
})

2. 每次执行 .then 的时候都会自动创建一个新的 Promise

如果熟悉 javascript 的链式风格,那么你应该会感到很熟悉。但是对于一个初学者来说,可能就不会了。

在 Promise 中不论你使用 .then 或者 .catch 都会创建一个新的 Promise。这个 Promise 是刚刚链式调用的 Promise 和 刚刚加上的 .then / .catch 的组合。

让我们来看一个 🌰:

var statusProm = fetchServerStatus();

var promA = statusProm.then(r => (r.statusCode === 200 ? "good" : "bad"));

var promB = promA.then(r => (r === "good" ? "ALL OK" : "NOTOK"));

var promC = statusProm.then(r => fetchThisAnotherThing());

上面 Promise 的关系可以在流程图中清晰的描述出来:

需要特别注意的是 promApromBpromC 全部都是不同的但是有关联的 Promise。

我喜欢把 .then 想像成一个大型管道,当上游节点出现问题时,水就会停止流向下游。例如,如果 promB 失败,下游节点不会受到影响,但是如果 statusProm 失败,那么下游的所有节点都将受到影响,即 rejected

3. 对调用者来说,Promiseresolved/rejected 状态是唯一的

我认为这个是让 Promise 好好运行的最重要的事情之一。简单来说,如果在你的应用中 Promise 在很多不同的模块之间共享,那么当 Promise 返回 resolved/rejected 状态时,所有的调用者都会收到通知。

这也意味着没有人可以改变你的 Promise,所以可以放心的把它传递出去。

function yourFunc() {
  const yourAwesomeProm = makeMeProm();

  yourEvilUncle(yourAwesomeProm); // 无论 Promise 受到了怎样的影响,它最终都会成功执行

  return yourAwesomeProm.then(r => importantProcessing(r));
}

function yourEvilUncle(prom) {
  return prom.then(r => Promise.reject("destroy!!")); // 可能遭受的影响
}

通过上面的例子可以看出,Promise 的设计使得自身很难被改变。正如我上面所说的:"保持冷静,并将 Promise 传递下去"。

4. Promise 构造函数不是解决方案

我看到很多开发者喜欢用构造函数的风格,他们认为这就是 Promise 的方式。但这却是一个谎言,实际的原因是构造函数 API 和之前回调函数的 API 相似,而且这样的习惯很难改变。

如果你发现自己正在到处使用 Promise 构造函数,那你的做法是错的!

要真正的向前迈进一步并且摆脱回调,你需要小心谨慎并且最小程度地使用 Promise 构造函数。

让我们看一下使用 Promise 构造函数 的具体情况:

return new Promise((res, rej) => {
  fs.readFile("/etc/passwd", function(err, data) {
    if (err) return rej(err);
    return res(data);
  });
});

Promise 构造函数 应该只在你想要把回调转换成 Promise 时使用
一旦你掌握了这种创建 Promise 的优雅方式,它将会变的非常有吸引力。

让我们看一下冗余的 Promise 构造函数

☠️错误的

return new Promise((res, rej) => {
    var fetchPromise = fetchSomeData(.....);
    fetchPromise
        .then(data => {
            res(data); // 错误!!!
        })
        .catch(err => rej(err))
})

💖正确的

return fetchSomeData(...); // 正确的!

Promise 构造函数 封装 Promise 是多余的,并且违背了 Promise 本身的目的

😎高级技巧

如果你是一个 nodejs 开发者,我建议你可以看一看 util.promisify。这个方法可以帮助你把 node 风格的回调转换为 Promise。

const {promisify} = require('util');
const fs = require('fs');

const readFileAsync = promisify(fs.readFile);

readFileAsync('myfile.txt', 'utf-8')
  .then(r => console.log(r))
  .catch(e => console.error(e));