异步与同步相比,最难以掌控的就是异步的任务会什么时候完成和完成之后的回调问题。
在你不知道的javascript一书中,对于回调的信任问题做了阐述 当你使用第三方的库的方法处理回调时很有可能遇到以下信任内容:
怎么解决???? 这种信任问题该怎么办?
第一部分:Promise
1. Promise 基础
Promise是JavaScript中用于处理异步操作的解决方案。Promise 可以看作是一个代表了异步操作最终完成或失败的对象。
它代表了一个尚未完成但预期在将来完成的操作。使用Promise,可以避免所谓的“回调地狱”,即多层嵌套的回调函数,从而使代码更加清晰和易于维护。Promise将回调地狱的嵌套模式改为了链式调用模式,利于代码可读性和维护性。
代码示例:创建一个基本的Promise
javascript
代码解读
复制代码
let promise = new Promise(function(resolve, reject) {
// 异步操作代码
setTimeout(() => {
resolve("操作成功");
}, 1000);
});
promise.then((value) => {
console.log(value); // 输出:操作成功
});
2. Promise 的状态
一个Promise有三种可能的状态:
- pending(待定) :初始状态,既不是成功,也不是失败。
- fulfilled(已实现) :意味着操作成功完成。
- rejected(已拒绝) :意味着操作失败。
代码示例:展示不同状态的Promises
let fulfilledPromise = Promise.resolve('成功');
let rejectedPromise = Promise.reject('失败');
fulfilledPromise.then(value => console.log(value)); // 输出:成功
rejectedPromise.catch(error => console.log(error)); // 输出:失败
使用 Promise 构造函数来创建一个 Promise 对象。构造函数接收一个带有两个参数的回调函数作为参数,这两个参数分别是 resolve 和 reject。
resolve 用于将 Promise 对象的状态从 pending 变为 fulfilled(已实现),
reject 则用于将状态从 pending 变为 rejected (已拒绝)。
const p=new Promise(function(resolve,reject){
//延时器只是为了模拟异步操作
setTimeout(function(){
1+1===2//判断1+1等于2吗
?resolve("成功了")//异步操作成功调用resolve,并给成功的操作传了一个数据-成功了
:reject("失败了")//异步操作失败调用reject,并给失败的操作传了一个数据-失败了
},2000)
})
//p现在是一个promise实例,then的意思是:然后||接下来
//p.then代表,p执行完后呢||p执行完接下来干什么
//p.then()里面要写一个回调函数,回调函数的参数就是成功传来的数据
p.then(res=>{//这里的参数是调用resolve传来的数据
console.log(res)
})
//p.catch表示失败,与then一样,只是一个表示成功,一个表示失败
p.catch(res=>{//这里的参数是调用reject传来的数据
console.log(res)
})
3. 链式调用和错误处理
Promise的另一个优点是可以通过链式调用.then()和.catch()方法来处理复杂的异步流程。
代码示例:展示链式调用和错误处理
javascript
代码解读
复制代码
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
})
.then(result => {
console.log(result); // 输出 1
return result * 2;
})
.then(result => {
console.log(result); // 输出 2
return result * 3;
})
.then(result => {
console.log(result); // 输出 6
return result * 4;
})
.catch(error => {
console.log('捕获到错误:', error);
});
4. 拓展promise的方法 .all/.race/.any
Promise.all([promise1,promise2,promise3,...])
- 接收一个数组,数组的每一项都是一个 promise 对象
- 当数组中所有的 promise 的状态都达到 resolved 的时候,Promise.all的状态就会变成 resolved
- 如果其中有一个 promise 的状态变成了 rejected,那么 Promise.all 的状态就会变成 rejected
- 调用then方法时的结果成功的时候是回调函数的参数也是一个数组,按顺序保存着每一个promise对象resolve执行时的值。
( . allSettled 方法在所有输入的 Promise 实例都改变了状态(执行 resolve 回调或 reject 回调)后执行自身的 resolve 回调。)
Promise.race([promise1,promise2,promise3,...]) 竞速模式
- 但是与all不同的是,第一个promise对象状态变成resolved时自身的状态变成了resolved,第一个promise变成rejected自身状态就会变成rejected。第一个变成resolved的promsie的值就会被使用
- 但是在控制台输出 promise1 和 promise3 都显示“任务完成”的状态,也就是说“竞赛”输了的选手也还是会完成这场比赛
Promise.any()
- 它只要其中的一个
promise成功,就返回那个已经成功的promise。 - 如果
Promise.any()的所有输入promise都被rejected后,那么辅助函数返回的promise也会以错误集合的方式拒绝,该错误在一个特殊属性AggregateError中包含输入promise的拒绝原因:aggregateError.errors。
第二部分:Async/Await
async和await是建立在Promise之上的高级抽象,使得异步代码的编写和阅读更加接近于同步代码的风格。
async函数是generator函数的语法糖,是ES6的异步编程解决方案。async/await:ES7提出,generator+promise的语法糖。async await是对promise的优化,将异步的代码优化为了同步的写法。
Async 函数
通过在函数声明前加上async关键字,可以将任何函数转换为返回Promise的异步函数。这意味着你可以使用.then()和.catch()来处理它们的结果。
代码示例:创建一个async函数
async function asyncFunction() {
return "异步操作完成";
}
asyncFunction().then(value => console.log(value)); // 输出:异步操作完成
Await 关键字
await关键字只能在async函数内部使用。它可以暂停async函数的执行,等待Promise的解决(resolve),然后以Promise的值继续执行函数。
await 后面如果跟的是Promise 对象,则会等待该对象里面的函数执行完成;如果跟的不是 Promise 对象则会执行其后面的代码 / 等于后面的值。
代码示例:在async函数中使用await
async function asyncFunction() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("完成"), 1000)
});
let result = await promise; // 等待,直到promise解决 (resolve)
console.log(result); // "完成"
}
asyncFunction();
错误处理
在async/await中,错误处理可以通过传统的try...catch语句实现,这使得异步代码的错误处理更加直观。
代码示例:使用try...catch处理错误
async function asyncFunction() {
try {
let response = await fetch('http://example.com');
let data = await response.json();
// 处理数据
} catch (error) {
console.log('捕获到错误:', error);
}
}
asyncFunction();
实践应用
在实际应用中,async和await使得处理复杂的异步逻辑更加简单,尤其是在涉及多个依次执行的异步操作时。 前几年的版本时,浏览器执行过程中await 会阻塞后续代码,将后续的代码放入微任务队列中,但是现在的浏览器执行代码时会给紧跟在await关键字后的代码开小灶,相当于紧随在await关键字后的代码变成同步代码,但是再往后的代码依旧会被推入微任务队列。
在代码中的实际执行逻辑可以去这篇文章:# 浅谈JavaScript面试题:从进程与线程到事件循环机制!!!
第三部分:Promise与Async/Await的比较
虽然async/await在很多情况下可以提供更清晰和简洁的代码,但Promise也有其独特的优势。例如,处理多个并行异步操作时,Promise.all()通常是更好的选择。选择使用Promise或async/await通常取决于具体的应用场景和个人偏好。