关于 Promise 的 9 个提醒

关于 Promise 的 9 个提醒

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

《关于 Promise 的 9 个提醒》

这篇文章会给你一些如何改良与 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 的关联能够在流程图中清楚的形貌出来:
《关于 Promise 的 9 个提醒》

须要特别注重的是 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));

</div>

5. 运用 Promise.resolve

Javascript 供应了 Promise.resolve 要领,像下面的例子如许简约:

var similarProm = new Promise(res => res(5));
// ^^ 等价于
var prom = Promise.resolve(5);

它有多种运用状况,我最喜好的一种是能够把一般的(异步的)js 对象转化成 Promise。

// 将同步函数转换为异步函数
function foo() {
  return Promise.resolve(5);
}

当不确定它是一个 Promise 照样一个一般的值的时刻,你也能够做一个平安的封装。

function goodProm(maybePromise) {
  return Promise.resolve(maybePromise);
}

goodProm(5).then(console.log); // 5

var sixPromise = fetchMeNumber(6);

goodProm(sixPromise).then(console.log); // 6

goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, 注重,它会自动剖析一切的 Promise!

6.运用 Promise.reject

Javascript 也供应了 Promise.reject 要领。像下面的例子如许简约:

var rejProm = new Promise((res, reject) => reject(5));

rejProm.catch(e => console.log(e)) // 5

我最喜好的用法是提早运用 Promise.reject 来谢绝。

function foo(myVal) {
    if (!mVal) {
        return Promise.reject(new Error('myVal is required'))
    }
    return new Promise((res, rej) => {
        // 从你的大回调到 Promise 的转换!
    })
}

简朴来讲,运用 Promise.reject 能够谢绝任何你想要谢绝的 Promise。

鄙人面的例子中,我在 .then 内里运用:

.then(val => {
  if (val != 5) {
    return Promise.reject('Not Good');
  }
})
.catch(e => console.log(e)) // 如许是不好的

注重:你能够像 Promise.resolve 一样在 Promise.reject 中通报任何值。你常常在失利的 Promise 中发明 Error 的缘由是由于它重要就是用来抛出一个异步毛病的。

7. 运用 Promise.all

Javascript 供应了 Promise.all 要领。像 … 如许的简约,好吧,我想不出来例子了😁。

在伪算法中,Promise.all 能够被归纳综合为:

吸收一个 Promise 数组

    然后同时运转他们

    然后比及他们悉数运转完成

    然后 return 一个新的 Promise 数组

    他们个中有一个失利或许 reject,都能够被捕捉。

下面的例子展现了一切的 Promise 完成的状况:

var prom1 = Promise.resolve(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => { // 注重,这里被剖析成一个数组
    console.log(val1); // 5
    console.log(val2.statusCode); // 200
})

下面的例子展现了当他们个中一个失利的状况:

var prom1 = Promise.reject(5);
var prom2 = fetchServerStatus(); // 返回 {statusCode: 200} 的 Promise

Proimise.all([prom1, prom2])
.then([val1, val2] => {
    console.log(val1); 
    console.log(val2.statusCode); 
})
.catch(e =>  console.log(e)) // 5, 直接跳转到 .catch

注重:Promise.all 是很智慧的!假如个中一个 Promise 失利了,它不会比及一切的 Promise 完成,而是马上中断!

8. 不要畏惧 reject,也不要在每一个 .then 背面加冗余的 .catch

我们是否是会常常忧郁毛病会在它们之间的某处被吞噬?

为了战胜这个恐惊,这里有一个简朴的小提醒:

让 reject 来处置惩罚上游函数的题目。

在抱负的状况下,reject 要领应当是运用的泉源,一切的 reject 都邑向下通报。

不要畏惧像下面如许写

return fetchSomeData(...);

如今假如你想要处置惩罚函数中 reject 的状况,请决定是处理题目照样继承 reject。

💘 处理 reject

处理 reject 是很简朴的,在 .catch 不管你返回什么内容,都将被假定为已处理的。但是,假如你在 .catch 中返回 Promise.reject,那末这个 Promise 将会是失利的。

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
        return 5;  // <-- 从新使要领一般运转
})
.then(r => {
    console.log(r); // 5
})
.catch(e => {
    console.error(e); // 这个要领永久不会被挪用 :)
})

💔谢绝一个 reject

谢绝一个 reject 是简朴的。不须要做任何事变。 就像我方才说的,让它成为其他函数的题目。通常状况下,父函数有比当前函数处置惩罚 reject 更好的要领。

须要记着的重要的一点是,一旦你写了 catch 要领,就意味着你正在处置惩罚这个毛病。这个和同步 try/catch的工作体式格局类似。

假如你确切想要阻拦一个 reject:(我强烈发起不要如许做!)

.then(() => 5.length) // <-- 这里会报错
.catch(e => {
  errorLogger(e); // 做一些毛病处置惩罚
  return Promise.reject(e); // 谢绝它,是的,你能够这么做!
})
.then(r => {
    console.log(r); // 这个 .then (或许任何背面的 .then) 将永久不会被挪用,由于我们在上面运用了 reject :)
})
.catch(e => {
    console.error(e); //<-- 它变成了这个 catch 要领的题目
})

.then(x,y) 和 then(x).catch(x) 之间的分界线

.then 吸收的第二个回调函数参数也能够用来处置惩罚毛病。它和 then(x).catch(x) 看起来很像,然则他们处置惩罚毛病的区分在于他们自身捕捉的毛病。

我会用下面的例子来申明这一点:

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}).catch(function(e) {
   console.error(e); // something wrong happened
});

.then(function() {
   return Promise.reject(new Error('something wrong happened'));
}, function(e) { // 这个回调处置惩罚来自当前 `.then` 要领之前的毛病
    console.error(e); // 没有毛病被打印出来
});

当你想要处置惩罚的是来自上游 Promise 而不是方才在 .then 内里加上去的毛病的时刻, .then(x,y) 变的很轻易。

提醒: 99.9% 的状况运用简朴的 then(x).catch(x) 更好。

9. 防止 .then 回调地狱

这个提醒是相对简朴的,只管防止 .then 里包括 .then 或许 .catch。置信我,这比你设想的更轻易防止。

☠️毛病的

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts)
           .then(r => r.text())
           .catch(err2 => console.error(err2))
  }
})

💖准确的

request(opts)
.catch(err => {
  if (err.statusCode === 400) {
    return request(opts);
  }
})
.then(r => r.text())
.catch(err => console.erro(err));

有些时刻我们在 .then 内里须要许多变量,那就别无选择了,只能再建立一个 .then 要领链。

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return promA
          .then(valA => {
              return promB.then(valB => hungryFunc(valA, valB)); // 很貌寝!
          })
})

我引荐运用 ES6 的解构要领混合著 Promise.all 要领就能够处理这个题目。

.then(myVal => {
    const promA = foo(myVal);
    const promB = anotherPromMake(myVal);
    return Promise.all([prom, anotherProm])
})
.then(([valA, valB]) => {   // 很好的运用 ES6 解构
    console.log(valA, valB) // 一切剖析后的值
    return hungryFunc(valA, valB)
})

注重:假如你的 node/浏览器/老板/认识许可,还能够运用 async/await 要领来处理这个题目。

我至心愿望这篇文章对你明白 Promise 有所协助。

《关于 Promise 的 9 个提醒》

    原文作者:周小肆
    原文地址: https://segmentfault.com/a/1190000018089931
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞