目录

JavaScript中的Promise

为什么要使用Promise

Promise简介

创建Promise

使用then、catch、finally

then()

catch()

finally()

Promise链

一个Promise的多个处理程序

Promise.all()

Promise.race()

Promise.any()

Promise.allSettled()

Promise.finally()

async/await

async关键字

await关键字

Promise.withResolvers

其它


JavaScript中的Promise

为什么要使用Promise

假设有一个函数getUsers用来获取一个用户对象列表,同时有一个函数findUser要从getUsers返回的用户列表中找到指定用户,那么这串代码可能是这样的:

function getUsers() {
  return [
    { username: 'john', email: '[email protected]' },
    { username: 'jane', email: '[email protected]' },
  ];
}

function findUser(username) {
  const users = getUsers(); 
  const user = users.find((user) => user.username === username);
  return user;
}

console.log(findUser('john')); // { username: 'john', email: '[email protected]' }

这串代码是同步代码,代码的执行是有顺序的:

  1. findUser()
  2. getUsers()
  3. console.log(...)

各个函数的调用并没有延迟,可是如果某个函数的调用需要延迟几秒,那么此时该如何呢?

function getUsers() {
    let users = [];
    setTimeout(() => {        
        users = [
            { username: 'john', email: '[email protected]' },
            { username: 'jane', email: '[email protected]' },
        ];
    }, 1000)
    return users;
}

function findUser(username) {
  const users = getUsers(); 
  const user = users.find((user) => user.username === username);
  return user;
}

console.log(findUser('john')); // undefined

因为users的返回需要1s时间,但是javascript的同步代码执行并不会等待这个1s,所以:“const users = getUsers()”这个代码相当于“const users”,故返回undefined

你可能想到使用回调函数来解决这个问题:

function getUsers(callback) {
    setTimeout(() => {
        callback([
            { username: 'john', email: '[email protected]' },
            { username: 'jane', email: '[email protected]' },
        ]);
    })
}

function findUser(username, callback) {
    getUsers((users) => {
        const user = users.find((user) => user.username);
        callback(user);
    })
}

findUser('john', console.log); // { username: 'john', email: '[email protected]' }

回调方法非常有效,但它使代码更难理解了。此外,如果函数数量增加,会遇到回调地狱的问题,为了解决这个问题,JavaScript提出Promise

Promise简介

Promise是一个对象,它封装了“异步操作”的结果。

Promise对象必须有一个状态,可以是下面三个状态之一:

  • 等待中(Pending
  • 已完成,带有值(Fulfilled
  • 已拒绝,带有原因(Rejected

一个Promise定义后,就是Pending状态,表示异步操作正在进行,根据异步操作的结果,状态将变为已完成已拒绝

创建Promise

使用Promise()构造函数创建Promise对象,Promise构造函数接受一个回调函数,该函数通常执行异步操作。

回调函数接受两个回调函数,分别名为“resolve”和“reject”,如果异步操作成功应手动调用resolve函数,将Promise状态从Pending更改为Fulfilled,反之变为Rejected

Promise达到“Fulfilled”或“Rejected”之后,它将保持该状态,无法再进入其他状态

使用then、catch、finally

then()

获取PromiseFulfilled状态下的值,可以调用then()方法,语法:

promise.then(onFulfilled, onRejected)

  1. onFulfilled:如果Promise已完成,调用该函数
  2. onRejected:如果Promise已拒绝,调用该函数

onFulfilled和Onrejected都是可选的

catch()

如果只想在Promise状态为拒绝时获取错误,可以使用catch()方法,语法:

promise.catch(onRejected)

finally()

有时,不管Promise成功或拒绝都想执行一段代码,可使用finally(),语法:

promise.finally()

  • finally没有参数

Promise链

ES7发布“await/async”后,不推荐使用Promise链,语法冗余且不易维护。

Promisethen方法支持return语法,不管return什么,默认都是一个新的Promise,所以如果你想的话可以这样做:

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 1000);
})

const a = p.then((result) => {
    console.log("result1", result);
    return result * 2;
})

console.log("a", a);

setTimeout(() => {
    console.log("a", a);
}, 2000);

结果:

你可能还会想到,那我是不是可以对“a”这个新Promise进行一个then操作?当然可以:

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 1000);
})

const a = p.then((result) => {
    console.log("result1", result);
    return result * 2;
})

a.then((result) => {
    console.log("result2", result);
    return result * 4;
})

如此then()方法不断返回新Promise,新Promise再不断调用then()方法,叫做Promise链。

一个Promise的多个处理程序

同一个Promise上多次调用then()方法时,这不是Promise链:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
})

p.then((result) => {
    console.log(result); // 10
    return result * 3;
})

p.then((result) => {
    console.log(result); // 10
    return result * 4;
});

结果:

Promise.all()

Promise.all()接受一个包含“可迭代对象”的Promise

Promise.all(iterable);

Promise.all()方法返回一个单一Promise,当所有Promise都已解析时,该Promise才会解析,返回的Promise解析结果是一个数组。

在此图中,promise1在t1时解析为值v1,promise2在t2时解析为值v2。因此,Promise.aPll(promise1, promise2)返回一个在t2时解析为一个数组的Promise,即[v1, v2]

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise1');
    resolve(10);
  }, 1 * 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise2');
    resolve(20);
  }, 2 * 1000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise3');
    resolve(30);
  }, 3 * 1000);
});

Promise.all([p1, p2, p3]).then((results) => {
  const total = results.reduce((p, c) => p + c);

  console.log(`结果: ${results}`);
  console.log(`总共: ${total}`);
});

结果:

如果其中一个Promise被拒绝,则Promise.all()方法立即返回一个被拒绝Promise

在此图中,promise2在t1时使用error拒绝,因此Promise.all()放回一个新的Promise,该Promise会立即使用相同的错误拒绝。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise1');
    resolve(10);
  }, 1 * 1000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise2 rejected');
    reject('Rejected caused kill');
  }, 2 * 1000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('promise3');
    resolve(30);
  }, 3 * 1000);
});

Promise.all([p1, p2, p3]).then((results) => {
  const total = results.reduce((p, c) => p + c);

  console.log(`结果: ${results}`);
  console.log(`总共: ${total}`);
});

结果:

总之,Promise.all()有助于聚合多个异步操作的结果

Promise.race()

Promise.race()接受一个包含promise的可迭代对象,并返回一个新的promise。该promise一旦其中一个promise fulfilled或reject,就会立即fulfilled或reject

Promise.race(iterable)

在该图中:

  • promise1在t1时用值v1 fulfilled
  • promise2在t2时用error reject
  • 由于promise1比promise2提前完成,因此Promise.race()返回的Promise状态是fulfiiled

Promise.any()

Promise.any()接受一个Promise对象列表作为可迭代对象。

在该图中:

  • promise1在t1时解析为值v1
  • promise2在t2时解析为值v2
  • Promise.any()返回一个Promise,该Promise在t1时为值v1,即promise1的结果

如果可迭代对象中所有Promise都失败,或者可迭代对象为空,则Promise.any()返回一个Promise,该Promise被一个包含所有拒绝原因的AggregateError拒绝。

在该图中:

  • promise1在t1时被error1拒绝
  • promise2在t2时被error2拒绝
  • Promise.any()返回一个Promise,该Promise在t2时被拒绝,并包含一个AggregateError,其中包含所有被拒绝的Promise的error1和error2

Promise.allSettled()

ES2020引入了Promise.allSettled()方法,它接受一个Promise列表,并返回一个新promise,该promise在所有输入promise结算后解析,无论是已解析还是已拒绝。

Promise.allSettled(iterable);

allSettled()返回的promise解析为一个数组,其中包含对象,每个对象描述输入的promise结果:

每个对象都有两个属性:statusvalue

  • status:可以是fulfilled或rejected
  • value:resolved或reject的原因

在本图中:

  • promise1在t1时拒绝error
  • promise2在t2时解析为value
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The first promise has resolved');
        resolve(10);
    }, 1 * 1000);

});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('The second promise has rejected');
        reject(20);
    }, 2 * 1000);
});

Promise.allSettled([p1, p2])
    .then((result) => {
        console.log(result);
    });

结果:

Promise.finally()

finally()在Promise状态解决时执行

promise.finally(onFinally)
  • onFinally:一个函数,当Promise解决时异步执行

finally()方法类似于try...catch...finally语句中的finally

promise
    .then(result => {
        // process the result
    })
    .catch(error => {
        // handle the error
    })
    .finally(() => {
        // clean up the resources
    });

async/await

async/awaitPromise语法糖,Promise的提出用来解决“回调地狱”问题,而async/await的提出用来解决“Promise链”问题。

它可以用来编写更像同步代码且更具可读性的异步代码。

async关键字

async关键字允许您定义一个处理异步操作的函数

要定义一个async函数,需要在函数关键字前添加async关键字:

async function sayHi() {
    return 'Hi';
}

async函数中返回一个Promise

await关键字

使用await关键字来等待一个Promise解决,无论是处于已解决状态还是拒绝状态,只能在async函数内使用await关键字

Promise.withResolvers

当创建一个新的Promise对象时,通常会将resolve和reject函数传递给Promise构造函数:

const promise = new Promise((resolve, reject) =>{
   // ...
});

仅仅允许在Promise内部调用resolve和reject,聪明的你可能想到创建外部变量来存储resolve和reject从而实现在Promise外部调用resolve和reject:

let resolve, reject;

const promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
});

Math.random() > 0.5 ? resolve("Success") : reject("Error");

使用Promise.withResolvers()函数,可以像这样简化代码:

const { promise, resolve, reject} = Promise.withResolvers();

Math.random() > 0.5 ? resolve("Success") : reject("Error");
  • promise:一个新的Promise对象
  • resolve:用于解决Promise的函数
  • reject:用于拒绝Promise的函数

PS:Promise.withResolvers()函数在ES2024后才可以使用

其它

更多JavaScript学习可以参考我的专栏:

JavaScript_是洋洋a的博客-CSDN博客

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐