Javascript 是一种单线程编程语言,支持异步执行,在不阻塞主线程的情况下满足并发执行的需求。Javascript promise 是处理异步执行的好方法。在JavaScript中,Promise是一种用于处理异步操作的对象。它代表了一个异步操作的最终完成或失败,并可以返回其结果。
Promise 如何运行
一个Promise
是一个代理,它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个promise,以便在将来的某个时间点提供该值。
Promise 三种状态
一个Promise必然处于以下几种状态之一:
- Pending(进行中): 初始化状态,表示异步操作尚未完成,也没有失败。
- Fulfilled(已成功): 表示异步操作成功完成。
- Rejected(已失败): 表示异步操作失败。
一个待定的 Promise 最终状态可以是已兑现并返回一个值,或者是已拒绝并返回一个原因(错误)。当其中任意一种情况发生时,通过 Promise 的 then 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝,这处理程序将被立即调用,因此在异步操作完成和绑定处理程序之间不存在竞态条件。
如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled)。
当一个 Promise 被创建后,它最初处于 Pending 状态。然后,它可以被异步操作解决为 Fulfilled 状态,表示操作成功完成,或者被拒绝为 Rejected 状态,表示操作失败。
Promise 的关键特性是它可以处理异步操作的结果,而不需要依赖回调函数。它通过链式调用的方式,将多个异步操作串联起来,使得代码更加清晰和易于理解。
在执行过程中,当一个 Promise 被解决为 Fulfilled 或 Rejected 状态时,它的状态将不再改变,并且它的结果(成功时的值或失败时的原因)将被传递给注册的 .then()
或 .catch()
方法。
Promise 的核心思想是将异步操作封装成一个对象,并提供统一的处理接口,使得异步代码更易于管理和组织。它可以有效地解决回调地狱(callback hell)和复杂的异步代码嵌套问题。
Promise 创建
Part 1
在JavaScript中,可以使用Promise
构造函数来创建Promise对象。Promise
构造函数接受一个参数,即执行器函数(executor)。执行器函数在创建Promise时立即执行。它接受两个参数:resolve
和reject
。这两个参数是由JavaScript运行时提供的函数,用于分别解决
或拒绝
Promise。
// 创建一个简单的Promise对象
let myPromise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
let success = true; // 模拟异步操作成功
if (success) {
resolve("Promise resolved!"); // 异步操作成功,调用resolve
} else {
reject("Promise rejected!"); // 异步操作失败,调用reject
}
}, 2000); // 2秒后执行
});
上诉示例中,我们创建了一个Promise对象,它代表了一个模拟的异步操作。在Promise的构造函数中,我们传递了一个执行器函数,这个函数接受两个参数:resolve
和reject
,它们是由JavaScript引擎提供的回调函数。在异步操作成功时,我们调用resolve
函数,并传递一个成功的消息;在异步操作失败时,我们调用reject
函数,并传递一个失败的消息。
Part 2
创建 Promise 后,可以使用该方法附加一个回调函数,在JavaScript中,Promise对象的.then()
方法用于附加一个或多个回调函数,以处理Promise对象的解析值(resolved value)。.then()
方法接受两个参数:一个是用于处理解析值的回调函数,另一个是用于处理拒绝值(rejected value)的回调函数。
let myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
let success = true; // 模拟异步操作成功
if (success) {
resolve("Promise resolved!"); // 异步操作成功,调用resolve
} else {
reject("Promise rejected!"); // 异步操作失败,调用reject
}
}, 2000); // 2秒后执行
});
// 使用.then()方法处理Promise对象的解析值和拒绝值
myPromise.then(
// 处理解析值的回调函数
(resolvedValue) => {
console.log("Resolved: " resolvedValue);
},
// 处理拒绝值的回调函数
(rejectedValue) => {
console.log("Rejected: " rejectedValue);
}
);
我们创建了一个简单的Promise对象myPromise
,并使用.then()
方法来附加两个回调函数:一个用于处理解析值的回调函数,另一个用于处理拒绝值的回调函数。
如果Promise对象在执行过程中被成功解析(resolved),第一个回调函数将被调用,并传递解析值作为参数。如果Promise对象在执行过程中被拒绝(rejected),则会调用第二个回调函数,并传递拒绝值作为参数。
.then()
方法是用于处理Promise对象解析值和拒绝值的关键方法,在异步操作的不同状态下执行相应的逻辑。
链式调用(Chained Promise)
链式调用(Chained Promise)是一种用于处理异步操作序列的技术,在JavaScript中,它允许按顺序执行多个异步操作,并且可以在每个操作完成后执行下一个操作。
代码语言:javascript复制Promise.prototype.then()
Promise.prototype.catch()
Promise.prototype.finally()
在JavaScript中,Promise.prototype.then(), Promise.prototype.catch(), 和 Promise.prototype.finally() 是 Promise 对象的原型方法,用于处理异步操作的结果或状态变化。
then(onFulfilled, onRejected)
- then() 方法用于注册对 Promise 对象成功(resolved)和失败(rejected)状态的处理函数。
- 它接受两个参数:onFulfilled 和 onRejected,分别是成功状态的处理函数和失败状态的处理函数。
- 如果 Promise 对象处于成功状态,将会调用 onFulfilled 处理函数;如果处于失败状态,将会调用 onRejected 处理函数。
- then() 方法返回一个新的 Promise 对象,可以继续进行链式调用。
catch(onRejected)
- catch() 方法用于注册对 Promise 对象失败(rejected)状态的处理函数。
- 它只接受一个参数 onRejected,是失败状态的处理函数。
- 如果 Promise 对象处于失败状态,将会调用 onRejected 处理函数。
- catch() 方法也返回一个新的 Promise 对象,可以继续进行链式调用。
finally(onFinally)
- finally() 方法用于注册在 Promise 对象无论成功还是失败状态下都要执行的处理函数。
- 它接受一个参数 onFinally,是无论成功或失败状态都要执行的处理函数。
- finally() 方法返回一个新的 Promise 对象,该对象的状态和值将与原始 Promise 对象一致。
- finally() 可以用于清理工作或者无论 Promise 成功还是失败都要执行的收尾操作。
下面是一个简单的示例,演示了这些方法的用法:
代码语言:javascript复制// 创建一个简单的Promise对象
let myPromise = new Promise((resolve, reject) => {
let success = true; // 模拟异步操作成功
if (success) {
resolve("Promise resolved!"); // 异步操作成功,调用resolve
} else {
reject("Promise rejected!"); // 异步操作失败,调用reject
}
});
// 使用then方法处理Promise对象的成功状态和失败状态
myPromise.then(
// 处理成功状态
(resolvedValue) => {
console.log("Resolved: " resolvedValue);
}
).catch(
// 处理失败状态
(rejectedReason) => {
console.log("Rejected: " rejectedReason);
}
).finally(
// 无论成功或失败都要执行的处理
() => {
console.log("Finally: This will always execute!");
}
);
在这个例子中,我们创建了一个简单的 Promise 对象 myPromise
,然后使用 then()
方法注册对成功状态的处理,catch()
方法注册对失败状态的处理,finally()
方法注册对无论成功或失败都要执行的处理。
多个then操作
根据需求创建多个带有.then()
方法的Promise链。在下面的示例中,我们模拟了一个简单的异步操作链,以演示Promise的串行执行特性:
// 异步操作1
function asyncOperation1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步操作1完成");
resolve("异步操作1的结果");
}, 1000);
});
}
// 异步操作2
function asyncOperation2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步操作2完成");
resolve("异步操作2的结果");
}, 2000);
});
}
// 异步操作3
function asyncOperation3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步操作3完成");
resolve("异步操作3的结果");
}, 1500);
});
}
// 创建一个Promise链
asyncOperation1()
.then(result1 => {
console.log("从异步操作1收到的结果:", result1);
return asyncOperation2(); // 返回第二个异步操作
})
.then(result2 => {
console.log("从异步操作2收到的结果:", result2);
return asyncOperation3(); // 返回第三个异步操作
})
.then(result3 => {
console.log("从异步操作3收到的结果:", result3);
console.log("所有操作成功完成!");
})
.catch(error => {
console.error("发生错误:", error);
});
输出:
Promise 和 Fetch API
Promise 和 Fetch API 在现代 JavaScript 中经常一起使用,特别是在进行网络请求时。Fetch API 是一种用于发送和接收网络请求的新的标准方式,而 Promise 则用于处理异步操作的结果。下面简要介绍了它们之间的关系和如何一起使用:
- Fetch API: Fetch API 提供了一种简单、灵活的方式来进行网络请求。它基于 Promise,并返回一个 Promise 对象,用于处理异步操作。Fetch API 使得发送和接收网络请求变得更加直观和易用。
- Promise: Promise 是一种用于处理异步操作的对象,它代表了一个异步操作的最终完成或失败。Promise 提供了一种更具结构化的方式来管理异步代码,并避免了回调地狱的问题。
如何一起使用
- 使用 Fetch API 发起网络请求:Fetch API 提供了
fetch()
方法来发送网络请求。fetch()
方法返回一个 Promise 对象,它在接收到网络响应时解析为 Response 对象。 - 处理 Fetch API 的结果:由于
fetch()
返回的是一个 Promise 对象,因此可以使用 Promise 的.then()
和.catch()
方法来处理网络请求的结果。在.then()
方法中可以处理成功的情况,而在.catch()
方法中可以处理失败的情况。
下面示例实现,展示了如何使用 fetch()
函数从远程 API 获取数据:
function fetchData() {
// 假设远程API的URL为https://example.com/api/data
const url = 'https://example.com/api/data';
return fetch(url) // 使用 Fetch API 发起网络请求
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json(); // 将响应解析为 JSON 数据
})
.then(data => {
// 在数据获取成功后执行一些操作
console.log('数据成功获取:', data);
// 返回获取的数据,以便后续操作
return data;
})
.catch(error => {
console.error('你的取回操作出了点问题:', error);
throw error; // 抛出错误,使得调用 fetchData 的代码可以捕获并处理错误
});
}
// 使用fetchData函数获取数据
fetchData()
.then(data => {
// 在这里对获取的数据执行进一步操作
console.log('获取数据的进一步处理:', data);
})
.catch(error => {
// 捕获并处理fetchData函数可能抛出的错误
console.error('获取数据时出错:', error);
});
在这个示例中,fetchData()
函数使用 Fetch API 从远程 API 获取数据,并在成功获取数据后对其执行一些操作,例如打印数据到控制台。然后,返回获取的数据以便后续操作。在调用 fetchData()
函数的代码中,可以使用 .then()
方法来处理成功获取数据后的进一步操作,并使用 .catch()
方法来捕获可能的错误。
Promise 取消
在现代 JavaScript 中 - 不可以,一旦 Promise 创建,就无法取消它。它将执行其代码并解析或拒绝,并且没有内置的方法来取消操作。
可以使用一些技术来模拟取消:
- 超时:如果解决时间过长,可以使用超时来拒绝 Promise。如果要发出网络请求并希望限制它所花费的时间,则此技术非常有用。
- 中止网络请求:可以使用中止控制器中止网络请求。Fetch API 提供了一个 AbortController API,允许在网络请求完成之前取消该请求。
- 使用标志位:可以在代码中使用标志来模拟取消。可以将该标志设置为 true 以指示应取消该操作,然后在 Promise 代码中检查该标志以确定是继续还是拒绝 Promise。
虽然 JavaScript 的 Promise 本身不直接支持取消操作,但可以通过上述方法实现类似的行为。需要根据具体情况和需求选择最合适的方法来管理和取消 Promise。
Promise Bluebird 取消
Bluebird 是一个流行的 JavaScript Promise 库,它提供了高级功能,包括 Promise 取消。Promise 取消是取消 Promise 的功能,这对于取消正在进行或长时间运行的异步操作非常有用。
在 Bluebird 的帮助下,使用该Promise.cancel()
方法实现了 Promise 取消。此方法不是标准 Promise API 的一部分,而是特定于 Bluebird。
要在 Bluebird 中使用 Promise 取消,需要使用new Promise()
构造函数创建一个可取消的 Promise,并将取消函数作为参数传递。取消 Promise 时将调用取消函数。
首先,需要安装 Bluebird 库。可以使用 npm 进行安装:
代码语言:javascript复制npm install bluebird
然后,可以使用以下方式在项目中引入 Bluebird 库:
代码语言:javascript复制const Promise = require('bluebird');
接下来,让我们看一个简单的示例,演示如何在 Bluebird 中取消 Promise:
代码语言:javascript复制// 引入 Bluebird 库
var Promise = require("bluebird");
// 创建一个 Promise,模拟一个异步操作,比如一个网络请求
var asyncOperation = new Promise(function(resolve, reject) {
// 模拟一个异步操作,比如一个网络请求
setTimeout(function() {
// 假设异步操作成功
resolve("操作成功");
}, 1000);
});
// 创建一个 Promise,模拟一个定时取消操作
var cancellationPromise = new Promise(function(resolve, reject, onCancel) {
// 设置一个定时器来模拟取消操作
var timer = setTimeout(function() {
// 假设在指定时间后,取消操作触发
resolve("取消操作成功");
}, 500);
// 注册取消操作的回调函数
// onCancel(function() {
// // 在取消操作时,清除定时器
// clearTimeout(timer);
// reject(new Error("操作被取消"));
// });
});
// 使用 Promise.race() 来竞速两个 Promise,哪个先完成就采用哪个结果
Promise.race([asyncOperation, cancellationPromise])
.then(function(result) {
console.log("结果:", result);
})
.catch(function(error) {
console.error("错误:", error.message);
});
// 在某些情况下,比如用户点击了取消按钮,我们可以取消异步操作
// 这里只是一个示例,在真实场景中,你可能需要根据具体情况来触发取消操作
// 这里只是演示如何触发取消操作
cancellationPromise.cancel();
这个例子中,我们创建了两个 Promise,一个模拟了一个异步操作 (asyncOperation
),另一个 (cancellationPromise
) 模拟了一个定时取消操作。
我们使用 Promise.race()
来让它们竞速,如果 cancellationPromise
在 asyncOperation
之前完成,那么就会执行取消操作。
在最后的示例中,我们调用了 cancellationPromise.cancel()
来模拟取消操作。
Promise 并发
Promise
类提供了四个静态方法来促进异步任务的并发:
- Promise.all():在所有传入的 Promise 都被兑现时兑现;在任意一个 Promise 被拒绝时拒绝
- Promise.any():在任意一个 Promise 被兑现时兑现;仅在所有的 Promise 都被拒绝时才会拒绝。
- Promise.race() :在任意一个 Promise 被敲定时敲定。换句话说,在任意一个 Promise 被兑现时兑现;在任意一个的 Promise 被拒绝时拒绝。
- Promise.allSettled():在所有的 Promise 都被敲定时兑现。
Promise.all() 方法
在 JavaScript 中,可以使用 Promise.all() 方法来并行处理多个 Promise。Promise.all() 方法接受一个 Promise 数组作为参数,并在所有 Promise 都成功解决后才返回成功,或者在任何一个 Promise 被拒绝时返回失败。
以下是一个简单的示例,演示如何使用 Promise.all() 方法来并行处理多个 Promise:
代码语言:javascript复制// 异步操作1
function asyncOperation1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作1的结果");
}, 1500);
});
}
// 异步操作2
function asyncOperation2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作2的结果");
}, 2000);
});
}
// 异步操作3
function asyncOperation3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("异步操作3的结果");
}, 1000);
});
}
// 并行处理多个 Promise
Promise.all([asyncOperation1(), asyncOperation2(), asyncOperation3()])
.then((results) => {
console.log("所有操作都成功完成!");
console.log("结果:", results);
})
.catch((error) => {
console.error("发生错误:", error);
});
在这个示例中,我们定义了三个异步操作函数:asyncOperation1()
、asyncOperation2()
和 asyncOperation3()
。每个异步操作函数返回一个 Promise 对象,模拟了一些异步操作,并在一定的延迟后解决 Promise。
然后,我们使用 Promise.all() 方法来并行处理这三个 Promise,将它们放入一个数组中作为参数传递给 Promise.all() 方法。当所有 Promise 都成功解决时,.then() 方法将被调用,接收一个包含所有结果的数组;如果任何一个 Promise 被拒绝,.catch() 方法将被调用,接收拒绝的原因。
这样,使用 Promise.all() 方法可以很方便地在 JavaScript 中并行处理多个 Promise,提高了异步操作的效率。
Promise.any() 方法
Promise.any()
方法是一个用于处理多个 Promise 的方法,它在给定的 Promise 中至少有一个解决(resolved)时解决,如果所有的 Promise 都被拒绝(rejected)则返回一个拒绝。
与 Promise.all()
和 Promise.race()
不同,Promise.any()
在至少有一个 Promise 被解决时就会解决,而不是等待所有 Promise 都解决。只要至少有一个 Promise 解决,Promise.any()
就会返回一个解决的 Promise,并且会忽略其它所有被拒绝的 Promise。
语法如下:
代码语言:javascript复制Promise.any(iterable);
iterable
是一个可迭代对象,通常是一个数组,包含多个 Promise 对象。
以下是一个示例,演示了 Promise.any()
方法的使用:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 1 被拒绝');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 2 被解决');
}, 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3 被解决');
}, 3000);
});
Promise.any([promise1, promise2, promise3])
.then((value) => {
console.log(value); // 将会输出 'Promise 2 被解决'
})
.catch((error) => {
console.log(error); // 所有 Promise 都被拒绝
});
在这个示例中,虽然 promise1
被拒绝,但 promise2
和 promise3
至少有一个被解决,因此 Promise.any()
返回的是一个解决的 Promise,它的值是 promise2
的解决值。
Promise.race() 方法
Promise.race()
方法是一个用于处理多个 Promise 的方法,它在给定的 Promise 中,只要有一个 Promise 解决(resolved)或被拒绝(rejected)时,就会返回一个新的 Promise。
语法如下:
代码语言:javascript复制Promise.race(iterable);
iterable
是一个可迭代对象,通常是一个数组,包含多个 Promise 对象。
Promise.race()
方法返回一个新的 Promise,它的状态和值取决于第一个解决的或拒绝的 Promise。如果第一个解决的 Promise,那么新的 Promise 将解决,并返回第一个解决的 Promise 的值;如果第一个被拒绝的 Promise,那么新的 Promise 将被拒绝,并返回第一个被拒绝的 Promise 的原因。
以下是一个示例,演示了 Promise.race()
方法的使用:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 被解决');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 2 被解决');
}, 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 3 被拒绝');
}, 3000);
});
Promise.race([promise1, promise2, promise3])
.then((value) => {
console.log(value); // 将会输出 'Promise 1 被解决',因为它是第一个解决的 Promise
})
.catch((error) => {
console.log(error); // 如果 promise3 是第一个被拒绝的,这里会输出 'Promise 3 被拒绝'
});
在这个示例中,promise1
是最快解决的,所以新的 Promise 将解决,并返回 promise1
的解决值。即使后面的 Promise promise2
和 promise3
也会解决或被拒绝,但它们的状态不会影响新的 Promise 的状态。
Promise.allSettled() 方法
Promise.allSettled()
方法是一个用于处理多个 Promise 的方法,它在所有的 Promise 都已经被解决(resolved)或被拒绝(rejected)后返回一个新的 Promise。
与 Promise.all()
方法不同,Promise.allSettled()
不会在有任何一个 Promise 被拒绝时返回一个拒绝的 Promise。相反,它会等待所有的 Promise 都被解决,并返回一个包含每个 Promise 结果的数组,每个结果都是一个对象,包含有状态(fulfilled 或 rejected)和对应的值或原因。
语法如下:
代码语言:javascript复制Promise.allSettled(iterable);
iterable
是一个可迭代对象,通常是一个数组,包含多个 Promise 对象。
以下是一个示例,演示了 Promise.allSettled()
方法的使用:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 resolved');
}, 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2 rejected');
}, 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3 resolved');
}, 3000);
});
Promise.allSettled([promise1, promise2, promise3])
.then((results) => {
console.log(results);
/*
输出:
[
{ status: 'fulfilled', value: 'Promise 1 resolved' },
{ status: 'rejected', reason: 'Promise 2 rejected' },
{ status: 'fulfilled', value: 'Promise 3 resolved' }
]
*/
});
在这个示例中,promise1
和 promise3
被解决,而 promise2
被拒绝。因此,Promise.allSettled()
返回一个数组,其中包含每个 Promise 的结果。对于解决的 Promise,结果对象包含 status
属性为 'fulfilled'
和 value
属性包含解决的值;对于被拒绝的 Promise,结果对象包含 status
属性为 'rejected'
和 reason
属性包含拒绝的原因。
Promise 的好处
Promise 在 JavaScript 中有许多好处,特别是在处理异步代码时。以下是一些 Promise 的好处:
- 更清晰的异步代码结构: Promise 提供了一种更具结构化的方式来组织异步代码,避免了回调地狱(callback hell)的问题。通过链式调用 .then() 方法,代码更加清晰、易读。
- 更好的错误处理机制: Promise 具有 .catch() 方法,可以捕获 Promise 链中任何地方发生的错误。这使得错误处理更加集中和可控,而不必在每个异步操作中都编写独立的错误处理逻辑。
- 避免回调嵌套: 使用 Promise 可以避免回调函数的嵌套问题,使代码更具可维护性。这样的代码结构更容易理解,减少了代码膨胀和复杂性。
- 更容易实现并行和串行操作: Promise 提供了 Promise.all() 和 Promise.race() 方法,使得并行执行多个异步操作和选取最快完成的操作变得更加容易。
- 更好的异步错误堆栈: Promise 在发生错误时会生成更详细的错误堆栈信息,有助于更容易追踪和调试异步代码中的问题。
- 提高代码可读性: Promise 的语法和链式调用的方式使得代码更易读,更贴近自然语言,提高了代码的可读性和可维护性。
- 更好的代码组织: Promise 提供了一种将异步操作组织成清晰顺序的方式,使得代码逻辑更加组织有序,易于理解和维护。
- 与现代 Web API 配合良好: 许多现代的 Web API(例如 Fetch API)返回的是 Promise 对象,使用 Promise 可以更方便地与这些 API 进行交互。
总的来说,Promise 提供了一种强大而灵活的机制,使得异步代码的处理更加简便、可读,并且在错误处理、代码组织和可维护性方面都带来了显著的改进。这也是为什么 Promise 成为现代 JavaScript 异步编程的标准之一。
结论
Promise 是 JavaScript 中一种强大的异步编程工具,它为处理异步操作提供了一种优雅、可控的解决方案,使得编写高效且可维护的异步代码变得更加容易。在现代 JavaScript 开发中,Promise 已经成为处理异步操作的标准方式之一。