📅  最后修改于: 2023-12-03 14:42:34.210000             🧑  作者: Mango
在 JavaScript 中,Promise 是一种非常重要的异步编程的方式,它可以解决回调地狱的问题,让代码更加简洁易懂。而 Promise 链则是 Promise 的一种组合方式,可以让多个 Promise 按顺序执行,让程序员更加容易掌控程序的执行流程。
Promise 链是由多个 Promise 按顺序执行而成的,这些 Promise 执行时可以相互依赖,可以将上一个 Promise 的结果传递给下一个 Promise。
一个简单的 Promise 链可以这么写:
Promise.resolve()
.then(() => {
return 1;
})
.then((value) => {
console.log(value); // 1
return value + 1;
})
.then((value) => {
console.log(value); // 2
return value + 1;
})
.then((value) => {
console.log(value); // 3
});
在上面这个例子中,Promise 的链式调用是通过 then
方法实现的。在第一个 Promise 里,返回了一个值 1,这个值会被传递到第二个 Promise 里的回调函数中;同理,后续的 Promise 中也是如此。
在实际的开发中,Promise 链可以帮助我们完成很多耗时的异步操作。例如,通过 Promise 链来上传用户头像:
const uploadAvatar = (file) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('post', '/api/user/avatar');
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = () => {
reject(new Error('Network Error'));
};
xhr.send(file);
});
}
const user = {
avatar: null
};
uploadAvatar(file)
.then(url => {
user.avatar = url;
return updateUser(user)
})
.then(() => {
console.log('用户头像上传成功');
})
.catch(error => {
console.error(error);
});
在这个例子中,我们先通过 uploadAvatar
函数来上传用户头像。上传成功后,我们将头像的 URL 赋值给用户对象,然后再通过 updateUser
函数来更新用户信息。如果这个 Promise 链中的任何一个 Promise 被 reject 了,后续的 Promise 都会被跳过,直接进入 catch 中。
在实现 Promise 链时,我们需要注意以下几点:
下面是一个简单的 Promise 链的实现:
class MyPromise {
constructor(executor) {
this.status = 'pending';
this.data = null;
this.onResolveCallbacks = [];
this.setRejectCallbacks = [];
const resolve = (data) => {
if (this.status === 'pending') {
this.status = 'resolved';
this.data = data;
this.onResolveCallbacks.forEach((callback) => {
callback(this.data);
});
}
};
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.data = error;
this.setRejectCallbacks.forEach((callback) => {
callback(this.data);
});
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onResolve, onReject) {
onResolve = typeof onResolve === 'function' ? onResolve : (value) => value;
onReject = typeof onReject === 'function' ? onReject : (error) => { throw error };
const promise = new MyPromise((resolve, reject) => {
const resolveCallback = (result) => {
try {
const value = onResolve(result);
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
} catch (error) {
reject(error);
}
};
const rejectCallback = (error) => {
try {
const value = onReject(error);
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
} catch (error) {
reject(error);
}
};
if (this.status === 'resolved') {
setTimeout(() => {
resolveCallback(this.data);
});
}
if (this.status === 'rejected') {
setTimeout(() => {
rejectCallback(this.data);
});
}
if (this.status === 'pending') {
this.onResolveCallbacks.push(resolveCallback);
this.setRejectCallbacks.push(rejectCallback);
}
});
return promise;
}
catch(onReject) {
return this.then(null, onReject);
}
static resolve(value) {
if (value instanceof MyPromise) {
return value;
} else {
return new MyPromise((resolve) => resolve(value));
}
}
static reject(error) {
return new MyPromise((resolve, reject) => reject(error));
}
}
除了可以链式调用 Promise 之外,还可以通过 Promise.all 和 Promise.race 来组合多个 Promise 对象。
Promise.all 是一个将多个 Promise 对象合成为一个新的 Promise 对象的方法,所有 Promise 的状态都为 resolved 时,新的 Promise 状态才为 resolved,其中任意一个 Promise 的状态为 rejected 时,新的 Promise 的状态就变成 rejected。Example:
Promise.all([promise1, promise2, promise3])
.then(([result1, result2, result3]) => {
console.log(`结果1: ${result1}`);
console.log(`结果2: ${result2}`);
console.log(`结果3: ${result3}`);
})
.catch(error => {
console.error(error);
});
Promise.race 和 Promise.all 的用法基本类似,它的作用是多个 Promise 只要有一个完成,这个新的 Promise 状态就会改变。它们都是用于将多个异步操作按顺序执行的一种方式。
Promise.race([promise1, promise2, promise3])
.then((result) => {
console.log(`最终结果: ${result}`);
})
.catch(error => {
console.error(error);
});
通过学习 Promise 链的使用,我们可以更加容易地控制程序的执行流程,编写更加简洁优雅的异步代码;在 Promise 链中,我们需要注意回调函数的返回值,避免抛出异常导致的程序崩溃。同时,通过 Promise.all 和 Promise.race 的使用,我们可以将多个异步操作按顺序或者竞争的方式执行。
另外,在实际的开发中,可以直接使用第三方的 Promise 库,如 bluebird 等,这些库已经提供了非常丰富的异步 API,并且针对 Promise 链式调用做了大量的性能优化。