ES6 中 Promise
写在前面:经常用,但概念和使用经常现查现用,在这篇总结梳理一下
Promise 介绍
Promise 是异步编程的一种解决方案,可以替代传统的解决方案(回调函数和事件)。ES6 统一了用法,并原生提供了 Promise 对象。
具体区别如下:
- 使用回调函数处理异步操作: 当异步操作完成时,回调函数会被调用。回调函数的主要问题是它们可以导致回调地狱(callback hell),也就是嵌套回调的情况
function getData(callback) {
// 异步操作
setTimeout(() => {
const data = 'Hello, world!';
callback(data);
}, 1000);
}
getData((data) => {
console.log(data); // 输出:Hello, world!
});
- 使用 Promise 处理异步操作:
Promise 的主要优点是它可以使异步代码更加清晰,避免回调地狱。Promise 还提供了链式调用的能力,这使得异步代码可以像同步代码一样顺序执行。
function getData() {
// 返回一个 Promise
return new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const data = 'Hello, world!';
resolve(data);
}, 1000);
});
}
getData().then((data) => {
console.log(data); // 输出:Hello, world!
});
- 回调地狱例子
//----回调地狱例子----//
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('得到最终结果: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
//使用Promise
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);
Promise 的状态
作为对象 Promise 具有以下特点:
- 对象的状态不受外界影响
- 一旦状态改变了就不会再变,也就是说任何时候 Promise 都只有一种状态 Promise 对象仅有三种状态:
pending
(进行中)fulfilled
(已成功)rejected
(已失败)
Promise 的用法
Promise 的构建
Promise 对象是一个构造函数,用来生成 Promise 实例
const promise = new Promise(function(resolve, reject) {});
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve
和 reject
resolve
函数的作用是,将 Promise 的状态从 pending(进行中)更改为 fulfilled(已成功)。异步操作成功时,调用 resolve 函数,并将结果作为参数传递。
示例:
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
resolve('Operation successful');
}, 1000);
});
reject
函数的作用是,将 Promise 的状态从 pending(进行中)更改为 rejected(已失败)。异步操作失败时,调用 reject 函数,并将错误作为参数传递。
示例:
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
reject(new Error('Operation failed'));
}, 1000);
});
Promise 的实例方法
Promise 构建出来的实例存在以下方法:
then()
then 是实例状态发生改变时的回调函数,第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数
then 方法返回的是一个新的 Promise 实例,可以根据这一特性,不断的链式调用回调函数 代码例子:JavaScriptfunction greet () { var promise = new Promise (function(resolve, reject){ var greet = "hello world" resolve(greet) }) return promise } var p = greet().then(v => { console.log(v + '1') // 打印 hello world1 return v }) .then (v => { console.log(v + '2')// 打印 hello world2 }) .then (v => { console.log(v + '3')// 前面没有return v,此处 打印 undefined3 }) console.log(p) // Promise { <pending> }
catch()
当 Promise 的状态变为
rejected
时,catch 方法中的回调函数会被调用。这个回调函数接受一个参数,即reject
函数传递的错误。 注:Promise
对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止 代码例子:JavaScriptconst promise = new Promise((resolve, reject) => { // 异步操作 setTimeout(() => { reject(new Error('Operation failed')); }, 1000); }); promise .then((result) => { console.log(result); // 如果操作成功,这行代码不会执行 }) .catch((error) => { console.error(error); // 如果操作失败,打印错误信息 }); //----“冒泡”性质----// doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .catch(error => console.error(error)); // 捕获并处理任何在上面的 Promise 链中出现的错误
finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
finally() 的回调函数不接受任何参数,这意味着你无法知道 Promise 的最终状态。 代码例子:JavaScriptconst promise = new Promise((resolve, reject) => { // 异步操作 setTimeout(() => { const success = Math.random() > 0.5; // 随机成功或失败 if (success) { resolve('Operation successful'); } else { reject(new Error('Operation failed')); } }, 1000); }); promise .then((result) => { console.log(result); // 如果操作成功,打印 "Operation successful" }) .catch((error) => { console.error(error); // 如果操作失败,打印错误信息 }) .finally(() => { console.log('Operation complete'); // 无论成功失败,都打印 "Operation complete" });
其他构造函数方法
Promise
构造函数存在以下方法:
all()
Promise.all()
方法用于将多个Promise
实例,包装成一个新的Promise
实例 在以下两种情况下改变状态:- 当所有的
Promise
实例都变为fulfilled
状态时,Promise.all()
返回的Promise
实例变为fulfilled
状态,它的结果是一个数组,包含所有Promise
实例的结果。 - 如果参数中的任何一个
Promise
实例变为rejected
状态,Promise.all()
返回的Promise
实例就会立刻变为rejected
状态,它的结果是第一个被reject
的Promise
实例的结果。
- 当所有的
注:如果作为参数的 Promise
实例,自己定义了catch
方法,那么它一旦被rejected
,并不会触发Promise.all()
的catch
方法
- 代码示例:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // 输出:[3, 42, "foo"]
})
.catch((error) => {
console.error(error);
});
- 代码实现
//代码实现
//使用 JavaScript 的 Promise 和 Array.prototype.reduce 方法
function promiseAll(promises) {
return new Promise((resolve, reject) => {
//检查输入是否为数组
if (!Array.isArray(promises)) {
return reject(new TypeError('Arguments must be an array'));
}
//我们创建一个计数器和一个数组来跟踪已解决的 Promise
let resolvedCounter = 0;
let promiseNum = promises.length;
let resolvedValues = new Array(promiseNum);
//使用 Promise.resolve 来确保它是一个 Promise
//并添加一个 then 处理程序
// Promise 解决时,更新计数器和解决值数组。
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promises[i]).then((value) => {
resolvedCounter++;
resolvedValues[i] = value;
if (resolvedCounter === promiseNum) {
return resolve(resolvedValues);
}
}, (reason) => {
// 如果任何一个 promise 被 reject,则整个 Promise.all 被 reject
return reject(reason);
});
}
});
}
race()
Promise.race() 会在数组中的任何一个 Promise 实例率先改变状态时,立即将自己的状态改变为相同的状态,并将那个率先改变的 Promise 实例的返回值,作为自己的返回值
代码示例:
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value); // 输出:"two"
})
.catch((error) => {
console.error(error);
});
allSettled()
Promise.allSettled()
方法接受一组Promise
实例作为参数,包装成一个新的Promise
实例 只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束代码示例
JavaScriptconst promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => { setTimeout(reject, 100, 'foo'); }); Promise.allSettled([promise1, promise2]) .then((results) => { console.log(results); });
使用场景
- 将图片的加载写成一个 Promise,一旦加载完成,Promise 的状态就发生变化
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
- 通过链式操作,将多个渲染数据分别给个 then,或当下个异步请求依赖上个请求结果的时候,通过链式操作友好解决问题
// 各司其职
getInfo().then(res=>{
let { bannerList } = res
//渲染轮播图
console.log(bannerList)
return res
}).then(res=>{
let { storeList } = res
//渲染店铺列表
console.log(storeList)
return res
}).then(res=>{
let { categoryList } = res
console.log(categoryList)
//渲染分类列表
return res
})
- 通过 all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个 loading 即可
function initLoad(){
// loading.show() //加载loading
Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
console.log(res)
loading.hide() //关闭loading
}).catch(err=>{
console.log(err)
loading.hide()//关闭loading
})
}
//数据初始化
initLoad()
- 通过 race 可以设置图片请求超时
let imgLoad = new Promise(function(resolve, reject) {
let img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = 'http://example.com/my-image.png';
});
let timeout = new Promise(function(resolve, reject) {
setTimeout(reject, 5000, 'Image load timed out');
});
Promise.race([imgLoad, timeout])
.then(function() {
console.log('Image loaded successfully');
})
.catch(function(error) {
console.log(error);
});
参考
https://vue3js.cn/interview/es6/promise.htmlhttps://blog.csdn.net/zyf971020/article/details/127015351https://es6.ruanyifeng.com/#docs/promise