Promise

promise 基础语法

Promise 是 JavaScript 中处理异步操作的一种方式。它代表了一个异步操作的最终完成或失败,并且其返回值可用于进一步处理

  1. 创建一个promise对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 创建一个 Promise  
    const myPromise = new Promise((resolve, reject) => {
    // 异步操作,比如一个网络请求
    const success = true;

    if (success) {
    resolve('成功!'); // 如果操作成功,调用 resolve 并传递结果
    } else {
    reject('失败!'); // 如果操作失败,调用 reject 并传递错误信息
    }
    });
  2. 使用一个promise对象

    1
    2
    3
    4
    5
    6
    // 使用 Promise  
    myPromise.then((result) => {
    console.log(result); // 在操作成功时执行
    }).catch((error) => {
    console.log(error); // 在操作失败时执行
    });

promise三个状态

  1. Pending(进行中):初始状态,表示异步操作正在进行中
  2. Fulfilled(已成功):调用resolve函数,表示异步操作已经成功完成,并且返回了一个值。将会调用 then() 方法绑定的回调函数来处理结果。
  3. Rejected(已失败):调用reject函数,表示异步操作失败了,并返回了一个错误信息。将会调用 catch() 方法绑定的回调函数来处理错误。

当 Promise 的状态从 Pending 转变为 Fulfilled 或 Rejected 时,它就永远不会再改变。这个特性使得我们可以更好地管理和处理异步代码。

在 Promise 对象被创建时,它的初始状态是 pending。随后,通过调用 resolve() 方法可以将其状态转换为 fulfilled,表示异步操作成功完成,并传递一个值作为结果;而调用 reject() 方法则会将其状态转换为 rejected,表示异步操作发生错误或失败,并传递一个原因作为错误信息。

一旦 Promise 的状态发生变化(从 pending 转变到 fulfilled 或 rejected),则其状态不能再次改变。这是 Promise 的重要特性之一。当 Promise 处于 fulfilled 状态时,它会执行与之关联的 then() 回调函数;当处于 rejected 状态时,则会执行与之关联的 catch() 回调函数。

Promise 是一个构造函数 (类)

其实现类似于这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyPromise {  
constructor(executor) {
// 内部状态变量
this.state = 'pending';
this.value = undefined;

// resolve 和 reject 函数
this.resolve = function(value) {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
};

this.reject = function(reason) {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
}
};

// 执行传入的 executor 函数,并传递 resolve 和 reject 函数作为参数
try {
executor(this.resolve, this.reject);
} catch(error) {
reject(error);
}
}
}

状态转换

当执行 resolve 方法时,Promise 的状态会由 pending 转换为 fulfilled,并且将成功的数据存储在 value 属性中。

当执行 reject 方法时,Promise 的状态会由 pending 转换为 rejected,并且将错误信息存储在 value 属性中。

then() 和 catch() 方法

Promise 实例具有 then() 和 catch() 方法,在异步操作完成后可以通过这些方法注册回调来处理结果或错误。

1
2
3
4
5
6
7
8
let promise = new Promise((resolve, reject) => {  
setTimeout(() => resolve("done!"), 1000);
});

promise.then(
result => console.log(result), // 输出 "done!"
error => console.log(error)
);

链式调用

then() 方法返回一个新的 Promise 对象,因此可以进行链式调用(chaining)来处理多个异步操作。

1
2
3
4
5
6
let promise1 = new Promise((resolve, reject) => setTimeout(resolve(1),1000));  
let promise2 = new Promise((resolve, reject) => setTimeout(resolve(2),2000));

promise1.then(result1 => return result1 *2).then(result2 => console.log(result)); // 输出:2

promise2.then(result1=>return result *3).then(result3=>console.log(result)); // 输出:6

异步操作管理

异步任务发起后可立即返回一个 pending 状态的 promise 对象,并将 resolve 或者 reject 操作交给内部任务控制。

总结来说,Promise 是通过构造函数创建出来的对象,在内部管理着自身状态和值,在外部提供了方法以及接口供用户使用。其设计使得代码更加清晰易读、避免了“回调地狱”,并方便了对异步任务流程控制

then 处理

then() 方法用于注册回调函数来处理异步操作的结果或错误。then() 方法接受两个参数:onFulfilled 和 onRejected,分别代表在异步操作成功和失败时要执行的回调函数。

1
2
3
4
promise.then(onFulfilled, onRejected)
/*
其中,onFulfilled 是一个可选参数,表示在 Promise 由 pending 转换为 fulfilled 时要执行的回调函数;而 onRejected 也是一个可选参数,表示在 Promise 由 pending 转换为 rejected 时要执行的回调函数。
*/

以下是使用 then() 方法处理异步操作结果和错误的示例代码:

1
2
3
4
5
6
7
8
let promise = new Promise((resolve, reject) => {  
setTimeout(() => resolve("done!"), 1000);
});

promise.then(
result => console.log(result), // 输出 "done!"
error => console.log(error) // 不会被执行
);

catch 处理

p.then(inFulFilled, onRejected).then(inFulFilled, onRejected).catch()

catch可以全局捕获,即可以捕获前面所有then() 的错误

  • p.then(onFulfilled, onRejected)

    • p 是一个 Promise 对象,通过调用 then() 方法来注册在 Promise 完成(fulfilled)或拒绝(rejected)时要执行的回调函数。
    • onFulfilled 是一个可选参数,表示在 Promise 由 pending 转变为 fulfilled 时要执行的回调函数。
    • onRejected 也是一个可选参数,表示在 Promise 由 pending 转变为 rejected 时要执行的回调函数。
    • 当 Promise 状态变为 fulfilled 时,会执行 onFulfilled 回调函数;当状态变为 rejected 时,会执行 onRejected 回调函数。
  • .catch()

    • catch() 方法是 Promise 原型对象上的方法,用于捕获前面任何一个 Promise 链中产生的错误,即全局捕获

链式调用

fetchData() 返回一个 Promise,然后通过 .then() 方法链式调用处理数据。每个 .then() 都接收前一个 Promise 返回的结果,并返回一个新的 Promise,从而形成链式调用。如果任何一个 .then() 中出现了错误,可以通过 .catch() 捕获并处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// 假设有一个返回 Promise 的函数 fetchData 和对返回数据进行处理的函数 processData

fetchData()
.then(data => {
// 对获取的数据进行处理
return processData(data);
})
.then(processedData => {
// 处理后的数据继续操作或返回
return processedData;
})
.then(finalData => {
// 对最终数据进行其他操作
console.log(finalData);
})
.catch(error => {
// 捕获链中任何地方出现的错误
console.error(error);
});

resolve

Promise.resolve() 是一个静态方法,用于返回一个已解决的 Promise 对象。这个方法通常用于将非 Promise 值包装为一个 Promise,或者将一个已经解决的 Promise 再次返回。

  • 包装非 Promise 值:如果传入一个普通的值(例如,数字、字符串等),Promise.resolve(value) 会返回一个以该值为结果成功的 Promise。
  • 返回已解决的 Promise:如果传入一个已解决的 Promise,它会直接返回这个 Promise,而不进行任何处理。
  • 即使是 Promise 也会进行处理:即使传入的值是一个 Promise,该 Promise 的状态与 Promise.resolve 返回的 Promise 的状态是相同的。

普通用法

1
2
3
4
const promise1 = Promise.resolve(42);  
promise1.then(value => {
console.log(value); // 输出: 42
});

已解决的Promise

1
2
3
4
5
const existingPromise = Promise.resolve('Already resolved');  
const promise2 = Promise.resolve(existingPromise);
promise2.then(value => {
console.log(value); // 输出: Already resolved
});

异步操作

1
2
3
4
5
6
7
8
9
10
11
//当传入的值是一个 Promise 时,Promise.resolve 不会返回新的 Promise,而是直接返回原 Promise
const originalPromise = new Promise((resolve) => {
setTimeout(() => {
resolve('I am resolved!'); // 经过2秒解决
}, 2000);
});

const promise3 = Promise.resolve(originalPromise);
promise3.then(value => {
console.log(value); // 输出: I am resolved!
});

Promise 链接

1
2
3
4
5
6
7
8
9
//Promise.resolve() 也常用于 Promise 链中,以确保获得一个 Promise
function getData() {
return Promise.resolve(fetch('https://api.example.com/data'))
.then(response => response.json());
}

getData().then(data => {
console.log(data);
});

reject

用于返回一个带有拒绝状态的 Promise 对象。当调用 Promise.reject() 时,返回的 Promise 对象将以指定的原因(reason)被拒绝。

  1. 参数:Promise.reject() 方法接收一个参数作为 Promise 被拒绝的原因(reason)。这个参数可以是任何值,通常是一个 Error 对象或一个字符串,用于描述拒绝的原因。
  2. 返回值:Promise.reject() 返回一个带有拒绝状态的 Promise 对象,该对象的状态为 rejected,并且 rejection 值即为传入的原因参数。
  3. 使用场景:
    • 当需要显式地创建一个被拒绝的 Promise 对象时,可以使用 Promise.reject()
    • 可以在异步操作中模拟出现错误或异常情况,使得 Promise 进入 rejected 状态。
    • 通常与 .catch()Promise.catch() 一起使用,用于捕获并处理 Promise 链中的错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 假设有一个返回 Promise 的函数 fetchData 和对返回数据进行处理的函数 processData  

fetchData()
.then(data => {
// 对获取的数据进行处理
return processData(data);
})
.then(processedData => {
// 处理后的数据继续操作或返回
return processedData;
})
.then(finalData => {
// 对最终数据进行其他操作
console.log(finalData);
})
.catch(error => {
// 捕获链中任何地方出现的错误
console.error(error);
});

race

接收一个包含多个 Promise 对象的可迭代对象(比如数组),并返回一个新的 Promise。这个新的 Promise 将在第一个传入的 Promise 对象状态发生改变时,采用第一个发生改变的状态,并以此状态进行后续操作。

  1. 参数:Promise.race() 方法接收一个可迭代对象作为参数,通常是包含多个 Promise 对象的数组。如果传入非可迭代对象,则会导致 TypeError。
  2. 返回值:Promise.race() 返回一个全新的 Promise 实例。当传入的多个 Promises 中有任意一个完成(无论是 resolve 还是 reject),返回的新 Promise 就会采用该完成状态,并且其后续操作也将以此为基础。
  3. 使用场景:
    • 在多个异步任务中,只要有其中之一完成就能得到结果时,可以使用 Promise.race()
    • 可以设置超时机制,在一定时间内没有得到结果就放弃等待。
    • 针对某些情况下需要快速响应或者处理优先级问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const promise1 = new Promise((resolve, reject) => {  
// 创建一个新的 Promise 对象
// 这个 Promise 在 100 毫秒后通过 resolve 方法解决,并传递 'First' 作为解决值
setTimeout(resolve, 100, 'First');
});

const promise2 = new Promise((resolve, reject) => {
// 创建另一个新的 Promise 对象
// 这个 Promise 在 200 毫秒后通过 resolve 方法解决,并传递 'Second' 作为解决值
setTimeout(resolve, 200, 'Second');
});

Promise.race([promise1, promise2])
.then(value => {
// 使用 Promise.race() 处理多个 Promises,获取最早完成的结果
console.log(value);
// 输出 "First" 或 "Second" 中较早完成的值
});

all

用于将多个 Promise 实例包装成一个新的 Promise 实例。当传入的所有 Promise 都变为 resolved 状态时,Promise.all() 返回的 Promise 才会变为 resolved 状态;如果其中任何一个 Promise 变为 rejected 状态,则整个 Promise.all() 返回的 Promise 也会立即变为 rejected 状态。

  1. 参数:Promise.all() 方法接收一个可迭代对象作为参数,通常是包含多个 Promise 对象的数组。如果传入非可迭代对象,则会导致 TypeError。
  2. 返回值:Promise.all() 返回一个全新的 Promise 实例。当传入的所有 Promises 都成功完成时,返回的新 Promise 将以包含所有结果值的数组进行 resolve;如果任一 Promise 失败,则返回的 Promise 将以失败原因进行 reject。
  3. 使用场景:
    • 当需要等待多个异步操作都完成后才执行后续操作时,可以使用 Promise.all()
    • 用于并行执行多个异步任务,提高效率。
    • 可以将多个独立的异步任务组合成一个整体,便于管理和控制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise1 = new Promise((resolve, reject) => {  
setTimeout(resolve, 100, 'First');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'Second');
});

Promise.all([promise1, promise2])
.then(values => {
console.log(values); // 输出包含 'First' 和 'Second' 的数组
});
/*
promise1 和 promise2 分别在不同延时后 resolve,并通过 Promise.all([promise1, promise2]) 来获取所有 Promise 的结果进行后续处理。由于两个 Promise 都成功完成,返回的新 Promise 将以包含所有结果值的数组进行 resolve,输出 ['First', 'Second']。
*/

手写promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 定义一个名为 MyPromise 的构造函数  
function MyPromise(executor) {
// 初始化 Promise 的状态、值和原因
this.state = 'pending'; // 初始状态为 pending
this.value = undefined; // 成功时的返回值
this.reason = undefined; // 失败时的原因
this.onFulfilledCallbacks = []; // 存储成功回调的数组
this.onRejectedCallbacks = []; // 存储失败回调的数组

// 定义 resolve 函数,用于将 Promise 状态改为 fulfilled
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'; // 将状态改为 fulfilled
this.value = value; // 存储成功时的返回值
// 触发所有成功回调
this.onFulfilledCallbacks.forEach(callback => callback(this.value));
}
};

// 定义 reject 函数,用于将 Promise 状态改为 rejected
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'; // 将状态改为 rejected
this.reason = reason; // 存储失败时的原因
// 触发所有失败回调
this.onRejectedCallbacks.forEach(callback => callback(this.reason));
}
};

try {
// 执行传入的执行器函数,捕获可能抛出的异常并将 Promise 状态改为 rejected
executor(resolve, reject);
} catch (error) {
reject(error); // 捕获异常并将状态置为 rejected
}
}

// 定义 then 方法,用于注册成功和失败回调,并返回一个新的 Promise 对象
MyPromise.prototype.then = function(onFulfilled, onRejected) {

return new MyPromise((resolve, reject) => {
if (this.state === "fulfilled") {
setTimeout(() => {
try {
let x = onFulfilled ? onFulfilled(this.value) : this.value;
resolve(x); // 执行成功回调并将结果传递给下一个 Promise
} catch (e) {
reject(e); // 捕获异常并将状态置为 rejected
}
}, 0);
}

if (this.status === "rejected") {
setTimeout(() => {
try {
let x = onRejected ? onRejected(this.reason) : this.reason;
resolve(x); // 执行失败回调并将结果传递给下一个 Promise
} catch (e) {
reject(e); // 捕获异常并将状态置为 rejected
}
}, 0);
}
});
}

// 定义 catch 方法,用于捕获可能发生的错误并返回一个新的 Promise 对象
MyPromise.prototype.catch = function(onRejected) {
return new MyPromise((resolve, reject) => {
if (this.state === "rejected") {
setTimeout(() => {
try {
let x = onRejected ? onRejected(this.reason) : this.reason;
resolve(x); // 执行失败回调并将结果传递给下一个 Promise
} catch (e) {
// 错误处理逻辑,可以根据实际需求进行修改
reject(e); // 捕获异常并将状态置为 rejected
}
}, 0);
}
});
}

其他处理异步的方法

处理异步操作是 JavaScript 中常见的任务。有几种方法可以处理异步操作,包括使用回调函数、Promise、async/await 和事件监听器等。

  1. 回调函数:回调函数是一种传递给异步函数的函数,用于在异步操作完成后执行。回调函数通常作为异步函数的最后一个参数传递,并在操作完成时被调用。这种方式可以处理简单的异步操作,但当有多个异步操作需要处理时,回调函数嵌套会导致回调地狱,使代码难以理解和维护。
  2. Promise:Promise 是一种用于处理异步操作的对象。它表示一个异步操作的最终完成或失败,并提供了链式调用的方式来处理操作结果。Promise 有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。通过使用 Promise,可以更好地组织和管理异步代码,避免回调地狱的问题。
  3. async/await:async/await 是 ECMAScript 2017 引入的异步编程模型。它基于 Promise,并提供了一种更简洁的语法来处理异步操作。通过在函数前面添加 async 关键字,可以将函数声明为异步函数。在异步函数内部,可以使用 await 关键字来等待一个 Promise 对象的解决,并以同步的方式获取操作结果。这种方式使得异步代码看起来更像是同步代码,更易于理解和编写。
  4. 事件监听器:某些异步操作可能会触发事件,可以使用事件监听器来处理这些事件。通过注册事件监听器,可以在异步操作完成时执行相应的回调函数。这种方式常用于处理浏览器中的事件,如点击事件、加载事件等。