为什么 JavaScript Promises 很棒
什么是承诺?
在计算机科学中,承诺基本上是一个概念,它处理异步操作成功完成后或未成功完成时将在未来产生的值,并优雅地处理失败。
太多行话?
这是一张高水平的图片 -
你可能会借东西给朋友,并且足够信任他们,所以你不必停下手头上的事情等他们归还。你知道他们最终会归还,所以你可以安心地生活,直到东西(或者一个借口😅)归还。
虽然你会根据朋友的承诺最终采取行动,但你会把这份承诺留到承诺真正兑现的那一天。
这就是 1976 年首次创造“Promise”(计算机科学中)一词时所考虑的概念。
Promise 概念由 jQuery 延迟对象在 JavaScript 世界中流行起来,其概念与 Promise 类似,但与 ECMAScript 2015 中的当前 Promises 规范不同。
在它们被正式纳入 ECMAScript 2015 规范后,现在所有最新的浏览器和 Node 中都实现了它们,它们的作用是清理回调中的潜在混乱并简化流程。
那么回调函数呢?
回调也会在异步操作之后产生值,但是在另一个异步操作之后执行一个异步操作需要在另一个回调中调用回调,如下所示。
回调嵌套得越来越深,导致代码水平扩展,从而产生容易出错且令人困惑的代码块,可以理解为“回调地狱”。
承诺可以用于绕过这个问题。
使用 Promises 代替
可以使用其构造函数创建承诺:
let promise = new Promise(function(resolve, reject) {
// Your application logic goes here
});
构造函数中传入的函数是执行器函数,它接受两个参数:Resolved 和 Rejected。
这个函数里面的代码就是每次创建新的Promise时要执行的逻辑。
构造函数返回的 Promise 应该能够通知执行函数,处理异步操作,执行何时开始、完成或返回错误。
承诺的组成部分
一个承诺有一个状态和一个结果
一个承诺有 3 种状态:
-
待办的
-
已实现
-
被拒绝
JavaScript 中的待处理承诺与现实世界非常相似,是一种已执行但尚未完成的承诺,因此可以进入“已实现”或“已拒绝”状态。
已履行的承诺已得到解决或完成,表明结果成功。
被拒绝的承诺表示由于错误或超时而导致的结果不成功。
并且,已实现或已拒绝的承诺被称为已解决。
承诺的结果属性可以保存以下值:
-
undefined :当状态为 pending 时
-
value :当 resolve(value) 被调用时
-
error :当调用reject(error)时
解决和拒绝
承诺可以被解决并resolve()
置于完成状态,或者被拒绝并出现错误reject(new Error('Something went wrong'))
并置于拒绝状态。
let myPromise = new Promise(function(resolve, reject) {
resolve("This will be resolved"); // EXECUTED
reject(new Error('Something went wrong')); // ignored
resolve("Resolved again?"); // ignored
});
在这个例子中,在承诺实现之后,reject() 和 resolve() 被再次调用。
但是,一旦状态发生变化,任何进一步的拒绝和解决的呼吁都将被忽略。
处理承诺
处理程序函数.then()
、.catch()
、.finally()
允许使用承诺的函数在承诺被实现/拒绝时与执行器函数同步。
使用.then()
这被称为处理承诺的拒绝或履行,因此最多可以接受两个函数作为参数:
myPromise.then(
(result) => { // a successful outcome, promise fulfilled
console.log(result);
},
(error) => { // promise rejected
console.log(error);
}
);
或者如果只需要两个结果之一:
// logs SUCCESFUL outcomes only
myPromise.then(
(result) => {
console.log(result);
}
);
// logs ERROR outcomes only
myPromise.then(
null,
(error) => {
console.log(error);
}
);
使用.catch()
保留一个参数为空来.then()
处理错误并不是很有用。这是在.catch()
需要的时候。
以下示例getPromise()
是一个用户实现的函数,它接受一个 URL 并返回一个说明其结果的承诺。
let urlPromise = getPromise(BAD_URL);
const consumerFunc = () => {
promise.catch(error => console.log(error));
}
consumerFunc ();
使用.finally()
此处理程序在执行后进行清理(例如,关闭实时连接)并且无论承诺是被拒绝还是实现都会运行。
以下是.finally()
与其他处理程序一起使用的情况:
let isLoading = true;
isLoading && console.log('Loading...');
//Promise with outcome of URL
promise = getPromise(A_URL);
promise.finally(() => {
isLoading = false;
console.log(`Promise settled and loading is ${isLoading}`);
}).then((result) => {
console.log({result});
}).catch((error) => {
console.log(error)
});
-
.finally()
将设置isLoading
为false
。 -
如果 URL 有效,
.then()
则执行并result
记录。 -
如果URL无效,
error
则会被捕获、.catch()
执行并error
记录。 -
.finally()
无论承诺是已解决还是已拒绝,都会被调用。
链接 Promises
aPromise.then()
总是返回一个承诺,这让我们可以调用.then()
新的承诺,从而.then()
产生一系列传递单个承诺的方法。
A.then()
可以返回一个新的 Promise,也可以抛出一个错误。以下示例演示了这一点:
let myPromise = getPromise(URL_RETURNING_MULTIPLE_ITEMS);
promise.then(result => {
// extracts URL of oneItem
let oneItem = JSON.parse(result).results[0].url;
return oneItem; // returns the URL
}).then(oneItemURL => {
console.log(oneItemURL);
return getPromise(oneItemURL); // new promise returned
}).then(itemData => {
console.log(JSON.parse(itemData));
}).catch(error => {
console.log(‘Error caught. In the Catch block’, error);
});
在创建新承诺的地方,会将前一个 URL.then()
传递进去,getPromise()
返回新的承诺,然后解析该承诺,并将其发送到记录链中itemData
。
如果发现错误,.catch()
则会触发链中的并处理错误。
这些就是 Promises 如何用于异步函数的基础知识,从而产生干净、易理解且不易出错的代码。
下次再见,祝您编码愉快!
文章来源:https://dev.to/methmi/why-javascript-promises-are-awesome-4obk