9个有希望的承诺秘诀
1. 你可以在 .then 中返回一个 Promise
2. 每次执行 .then 时都会创建一个新的 Promise
3. Promise 为每个人解决/拒绝
4. Promise 构造函数不是解决方案
5. 使用 Promise.resolve
6.使用 Promise.reject
7. 使用 Promise.all
8. 不要害怕被拒绝
不要.catch
在每个 .then 后面添加多余的内容
9. 避免“.then”
Promise 用起来很棒!或者你同事也这么说。
本文将为您提供有关如何改善与承诺的关系的实用建议。
1. 你可以在 .then 中返回一个 Promise
让我把最重要的提示突出出来
是的!你可以在 .then 中返回一个 Promise
此外,返回的承诺将在下一个.then
.then(r => {
return serverStatusPromise(r); // this is a promise of { statusCode: 200 }
})
.then(resp => {
console.log(resp.statusCode); // 200; notice the automatic unwrapping of 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());
这里要注意的重要一点是promA
,promB
和promC
都是不同的承诺,但又相互关联。
我喜欢把 ing 想象.then
成一个巨大的管道,当父节点发生故障时,水就会停止流向子节点。例如,如果父节点promB
发生故障,其他节点不会受到影响;但如果statusProm
发生故障,所有节点都会受到影响rejected
。
3. Promise 为每个人解决/拒绝
我发现这是让 Promise 变得好用的关键因素之一。简单来说,如果一个 Promise 在应用的多个部分之间共享,那么当它被触发时,所有部分都会收到通知resolved/rejected
。
这也意味着没有人可以改变你的承诺,所以请放心地传递它,而不必担心。
function yourFunc() {
const yourAwesomeProm = makeMeProm();
yourEvilUncle(yourAwesomeProm); // rest assured you promise will work, regardless of how evil uncle consumes your promise
return yourAwesomeProm.then(r => importantProcessing(r));
}
function yourEvilUncle(prom) {
return prom.then(r => Promise.reject("destroy!!")); // your evil uncle
}
从上面的例子中,你可以看到,Promise 的设计使得任何人做坏事都变得困难。正如我上面所说,Keep calm and pass the promise around
4. Promise 构造函数不是解决方案
我见过很多开发者到处利用构造函数风格,以为这是在用 Promise 的方式。但这完全是谎言,真正的原因是构造函数 API 与经典的回调 API 非常相似,而且旧习难改。
如果你发现自己
Promise constructors
到处都在写字,那你就做错了!
为了真正向前迈一步并摆脱回调,您需要小心地减少所使用的 Promise 构造函数的数量。
让我们跳到 a 的实际用例Promise constructor
:
return new Promise((res, rej) => {
fs.readFile("/etc/passwd", function(err, data) {
if (err) return rej(err);
return res(data);
});
});
Promise constructor
仅当您要将回调转换为 Promise 时才应使用。
一旦您掌握了这种创建 Promise 的美妙方式,那么在其他已经 Promise 化的地方使用它就会变得非常诱人!
让我们看一个冗余的Promise constructor
☠️错误
return new Promise((res, rej) => {
var fetchPromise = fetchSomeData(.....);
fetchPromise
.then(data => {
res(data); // wrong!!!
})
.catch(err => rej(err))
})
💖正确
return fetchSomeData(...); // when it looks right, it is right!
用 包装承诺Promise constructor
只是多余的,并且违背了承诺本身的目的。
😎专业提示
如果你是Node.js用户,我推荐你试试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));
5. 使用 Promise.resolve
Javascript 提供了Promise.resolve
,这是一个简短的函数,可以编写如下内容:
var similarProm = new Promise(res => res(5));
// ^^ is equivalent to
var prom = Promise.resolve(5);
它有多个用例,我最喜欢的一个是能够将常规(同步)javascript 对象转换为承诺。
// converting a sync function to an async function
function foo() {
return Promise.resolve(5);
}
您还可以将其用作不确定是承诺还是常规值的值的安全包装器。
function goodProm(maybePromise) {
return Promise.resolve(maybePromise);
}
goodProm(5).then(console.log); // 5
goodProm(Promise.resolve(Promise.resolve(5))).then(console.log); // 5, Notice it unwraps all the layers of promises automagically!
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) => {
// your big callback to promie conversion!
})
}
简单来说,Promise.reject
在你想拒绝承诺的任何地方使用。
在下面的例子中,我使用.then
.then(val => {
if (val != 5) {
return Promise.reject('Not Good');
}
})
.catch(e => console.log(e)) // Not Good
Promise.reject
注意:你可以像 一样在里面放入任何值Promise.resolve
。你经常在被拒绝的 Promise 中发现它的原因Error
是,它主要用于抛出异步错误。
7. 使用 Promise.all
Javascript 提供了Promise.all,它是...的简写。好吧,我想不出这个😁。
在伪算法中,Promise.all
可以总结为
Takes an array of promises
then waits for all of them to finish
then returns a new Promise which resolves into an Array
catches if even a single fails/rejects.
以下示例显示所有承诺何时得到解决:
var prom1 = Promise.resolve(5);
var prom2 = fetchServerStatus(); // returns a promise of {statusCode: 200}
Proimise.all([prom1, prom2])
.then([val1, val2] => { // notice that it resolves into an Array
console.log(val1); // 5
console.log(val2.statusCode); // 200
})
当其中一个发生故障时,将显示以下内容:
var prom1 = Promise.reject(5);
var prom2 = fetchServerStatus(); // returns a promise of {statusCode: 200}
Proimise.all([prom1, prom2])
.then([val1, val2] => {
console.log(val1);
console.log(val2.statusCode);
})
.catch(e => console.log(e)) // 5, jumps directly to .catch
注意:Promise.all
很智能!如果出现拒绝,它不会等待所有 Promise 完成!任何 Promise 被拒绝时,它会立即中止,而无需等待其他 Promise 完成。
😎 ProtipPromise.all
不提供批量执行 Promise(并发)的方法,因为 Promise 的设计使其在创建时就立即执行。如果你想控制执行,我建议你尝试一下Bluebird.map。(感谢Mauro提供的这个技巧。)
8. 不要害怕被拒绝
不要.catch
在每个 .then 后面添加多余的内容
我们是否经常担心错误会在某处被吞噬?
为了克服这种恐惧,这里有一个非常简单的技巧:
使拒绝处理成为父函数的问题。
理想情况下,拒绝处理应该是应用程序的根源,所有承诺的拒绝都会流向它。
不要害怕写这样的东西
return fetchSomeData(...);
现在,如果您确实想在函数中处理拒绝,请决定是否要解决问题或继续拒绝。
💘解决拒绝
解决拒绝很简单,在 中, .catch
无论你返回什么,都会被认为是已解决的。但是有一个陷阱(双关语),如果你Promise.reject
在 中 返回 ,.catch
则 Promise 将被拒绝。
.then(() => 5.length) // <-- something wrong happenned here
.catch(e => {
return 5; // <-- making javascript great again
})
.then(r => {
console.log(r); // 5
})
.catch(e => {
console.error(e); // this function will never be called :)
})
💔拒绝拒绝 拒绝
拒绝很简单,什么都不做。正如我上面所说,让它成为其他函数的问题。通常情况下,父函数比当前函数有更好的方法来处理拒绝。
需要记住的是,一旦你写了 catch,就意味着你正在处理错误。这和同步的try/catch
工作原理类似。
如果您确实想拦截拒绝:(我强烈建议不要这样做!)
.then(() => 5.length) // <-- something wrong happenned here
.catch(e => {
errorLogger(e); // do something impure
return Promise.reject(e); // reject it, Yes you can do that!
})
.then(r => {
console.log(r); // this .then (or any subsequent .then) will never be called as we rejected it above :)
})
.catch(e => {
console.error(e); //<-- it becomes this catch's problem
})
.then(x,y) 和 then(x).catch(x) 之间的微妙区别在于,
then.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) { // callback handles error coming from the chain above the current `.then`
console.error(e); // no error logged
});
.then(x,y)
当你想要处理来自你正在执行的承诺的错误then
,而不想处理.then
刚刚附加到承诺链中的错误时,这真的很方便。
注意:99.9% 的情况下,使用更简单的会更好then(x).catch(x)
。
9. 避免“.then”
这个技巧很简单,尽量避免在.then
a.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);
}
return Promise.reject(err);
})
.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)); // very hungry!
})
})
我建议使用 ES6 解构功能来解决Promise.all
这个问题!
.then(myVal => {
const promA = foo(myVal);
const promB = anotherPromMake(myVal);
return Promise.all([prom, anotherProm])
})
.then(([valA, valB]) => { // putting ES6 destructing to good use
console.log(valA, valB) // all the resolved values
return hungryFunc(valA, valB)
})
注意:如果您的节点/浏览器/老板/意识允许,您也可以使用async/await来解决此问题!
我真的希望这篇文章能帮助您理解 Promises。
请查看我之前的博客文章。
如果您❤️这篇文章,请分享这篇文章来传播。
文章来源:https://dev.to/kepta/promising-promise-tips--c8f