告别回调地狱:用Promise优化你的异步代码

279 阅读3分钟

前言

JavaScript是一门单线程语言,这就意味着它一次只能按顺序执行一段代码。然而,在我们实际web开发应用中,总有一些耗费时间和性能的任务,而为了不阻塞主线程的进行,异步操作就诞生了(如网络请求,读写文件,定时器等),它允许我们等待一个异步操作完成时继续进行下面的任务。在es6之前,开发者主要依赖回调函数来管理异步函数逻辑,但这种方式容易导致“回调地狱”,即嵌套多层回调函数,使得代码难以阅读和维护。

为了解决这些问题,JS在es6中引入了Promise对象,它提供了一种更优雅的方式来处理异步任务,使得异步执行的顺序可控,并且让异步代码看起来更像是同步代码,提高了代码的可读性和可维护性。

Promise

Promise通过构造函数new Promise()创建,构造函数会接收一个执行器(executor),里面装的是耗时性的内容,执行器会在Promise对象实例化时立即执行

const p = new Promise(() => {
    console.log('a'); // 同步任务
    setTimeout(() => {
        console.log('b');
    }, 1000);
})
// 先输出a,再输出b

Promise的执行器函数中会接受两个参数,分别是:resolvereject它们是用于改变Promise状态的函数。

const p = new Promise((resolve,reject) => {

})

Promise具有3种状态,分别是pendingfulfilledrejected

Promise刚被创建时,它便为pending状态,可以看成是等待状态,它在等待异步操作的完成,只有当异步操作完成,并且调用resolve或者reject时才会发生改变。 它们可以通过改变Promise的状态后通过调用.then().catch()函数来优雅的处理异步的结果,并且可以捕获和处理可能出现的错误。

const p = new Promise(() => {
    console.log('a');
    setTimeout(() => {
        console.log('b');
    }, 1000);
})
console.log(p);

image.png

在这个例子中,我们是先打印的p再执行的异步操作,所以它会返回pending

const p = new Promise(() => {
    console.log('a');
    setTimeout(() => {
        console.log('b');
    }, 1000);
})
setTimeout(() => {
    console.log(p);
},2000);

image.png

在这个例子中,我们虽然完成了异步,但是并没有调用resolvereject改变状态,所以返回的还是pending

resolve(value):用于将Promise的状态从pending变为fulfilled表示异步操作成功完成。resolve中接受的一个参数value这个参数可以是任何类型的值,这个值会传递给then回调函数的第一个参数。如果resolve没有传递任何参数那么Promise的状态仍然会是fulfilled但是其值PromiseResult会为undefined

.then(onFulfilled, onRejected):其中有两个函数作为参数,第一个参数为Promise状态变为fulfilled时调用的回调函数,第二个则是变为rejected时调用的回调函数(可选)。你可以通过resolve传递参数给这两个回调函数来传递值和传递错误的原因。

const p = new Promise((resolve, reject) => {
    console.log('a');
    setTimeout(() => {
        console.log('b');
        resolve();
    }, 1000);
})
console.log(p);
p.then(() => {
    console.log('c');
    console.log(p);
})

如果我们此时在控制台打印输出,那么我们只能看到Promise{ undefined }所以我们要去浏览器中查看。

image.png

此时没有传入值,当我们为resolve传入值后,并且在.then()中传入后:

const p = new Promise((resolve, reject) => {
    console.log('a');
    setTimeout(() => {
        console.log('b');
        resolve('baby');
    }, 1000);
})
console.log(p);
p.then((data) => {
    console.log(data);
    console.log(p);
})

image.png

reject(reason):用于将Promise的状态从pending变为rejected表示异步操作成功失败。reject中接受的一个参数reason这个参数通常是一个错误对象或者字符串,表示失败的原因,这个值会传递给catch 或 then 回调函数的第二个参数。

.catch(onRejected):其中有一个函数参数,用来接受失败时传递的错误原因,你可以通过reject传递参数给它传递错误的原因。

const p = new Promise((resolve, reject) => {
    console.log('a');
    setTimeout(() => {
        console.log('b');
        reject('呜呜呜');
    }, 1000);
})
console.log(p);
p.catch((error) => {
    console.log(error);
    console.log(p);
})

image.png

Promise只能从pendingfulfilled或者rejected,并且一旦状态改变后,就不会再改变,且不可逆。