通过代码实例带你了解Promise

1. Promise基础概念

Promise是一种用来处理异步操作的简单对象,它代表了异步操作的最终完成或失败,并返回结果值或抛出异常。一个Promise一般有三种状态:pending、fulfilled和rejected。当Promise对象的异步操作成功时,它的状态变为fulfilled;当Promise对象的异步操作失败时,它的状态变为rejected。一旦一个Promise对象的状态变为fulfilled或rejected,它们就不会再发生变化,此后任何时候对Promise对象的任何操作都是无效的。

1.1 创建Promise

创建一个Promise对象非常简单,只需要调用Promise的构造函数,并传入一个函数作为参数。这个函数被称为“执行器函数”,它接受两个函数参数:resolve和reject。当Promise状态变为fulfilled时,执行器函数应该调用resolve函数并将结果作为参数传递给它,而当Promise状态变为rejected时,则应该调用reject函数并将错误作为参数传递给它。下面是一个简单的Promise示例:

const promise = new Promise((resolve, reject) => {

// 异步操作代码

if (/* 异步操作成功 */) {

resolve(result);

} else {

reject(error);

}

});

1.2 Promise链式调用

Promise一般都是通过链式调用来实现多个异步操作的有序执行,每个异步操作的结果都会被传递到下一个异步操作中。为了实现链式调用,Promise实例对象必须返回自身对象。下面是一个简单的Promise链式调用示例:

const promise = new Promise((resolve, reject) => {

// 异步操作代码

if (/* 异步操作成功 */) {

resolve(result1);

} else {

reject(error1);

}

}).then((result1) => {

// 处理result1

return result2; // 返回一个新的Promise

}).then((result2) => {

// 处理result2

return result3; // 返回一个新的Promise

}).catch((error) => {

// 处理错误

}).finally(() => {

// 总是执行的代码

});

在上面的示例中,promise对象中第一个执行器函数会返回一个新的promise对象,然后新的promise对象会被传递给第一个then方法。第一个then方法会处理新的promise对象成功状态下的返回值,并返回一个新的promise对象,然后新的promise对象会被传递给第二个then方法。第二个then方法也会处理新的promise对象成功状态下的返回值,并返回一个新的promise对象,依此类推。如果链式调用中任意一个promise对象被拒绝了,它后面的所有方法都会被跳过,直到遇到catch或finally方法。如果链式调用中没有catch方法,错误将会被抛出。

2. Promise高级用法

2.1 Promise.all

Promise.all方法可以接收一个 Promise 对象的iterable(如数组),返回一个新的 Promise 对象。当所有的 Promise 对象都变为成功状态时,Promise.all返回的 Promise 对象就变为成功状态,并将每个 Promise 对象的返回值作为一个数组返回给then方法;如果其中任一一个 Promise 对象变为失败状态,Promise.all返回的 Promise 对象就变为失败状态,并将第一个失败的 Promise 对象的错误信息返回给catch方法。

Promise.all([promise1, promise2, promise3])

.then((results) => {

// 处理结果

})

.catch((reason) => {

// 处理异常

})

在上面的示例中,Promise.all会等待promise1、promise2和promise3都变为成功状态时,才会调用then方法;如果其中一个或多个Promise对象变为失败状态,就会立即调用catch方法并返回失败的Promise对象的错误信息。

2.2 Promise.race

Promise.race方法和Promise.all方法类似,它也是接收一个 Promise 对象的iterable(如数组),返回一个新的 Promise 对象。当iterable中任意一个 Promise 对象变为成功状态时,Promise.race方法返回的 Promise 对象就变为成功状态,并将第一个 Promise 对象的返回值作为then方法的参数;如果其中任一一个 Promise 对象变为失败状态,Promise.race方法返回的 Promise 对象就变为失败状态,并将第一个失败的 Promise 对象的错误信息返回给catch方法。

Promise.race([promise1, promise2, promise3])

.then((result) => {

// 处理结果

})

.catch((reason) => {

// 处理异常

})

在上面的示例中,Promise.race会等待promise1、promise2和promise3中任意一个 Promise 对象变为成功状态或失败状态时,才会调用then方法或catch方法。

2.3 Promise.resolve

Promise.resolve方法可以接收一个value值或一个 Promise 对象,并返回一个新的 Promise 对象,这个 Promise 对象会立即变为成功状态,并将value值或者 Promise 对象的返回值作为then方法的参数。

Promise.resolve(value)

.then((result) => {

// 处理结果

})

在上面的示例中,Promise.resolve会立即返回一个成功状态的Promise对象,并将value作为then方法的参数。

2.4 Promise.reject

Promise.reject方法可以接收一个error错误信息,并返回一个新的 Promise 对象,这个 Promise 对象会立即变为失败状态,并将error错误信息作为catch方法的参数。

Promise.reject(error)

.catch((reason) => {

// 处理异常信息

})

在上面的示例中,Promise.reject会立即返回一个失败状态的Promise对象,并将error作为catch方法的参数。

3. Promise使用实例

3.1 实现图片预加载

下面是一个简单的实现图片预加载的示例,该示例使用Promise.all方法实现了多张图片预加载,当所有图片都被成功加载时,将它们一次性添加到页面中的img元素中。

const imageUrls = ['http://example.com/image1.jpg', 'http://example.com/image2.jpg', 'http://example.com/image3.jpg'];

const promises = imageUrls.map((imageUrl) => {

return new Promise((resolve, reject) => {

const image = new Image();

image.onload = () => {

resolve(image);

};

image.onerror = () => {

reject(new Error(`图片加载失败 ${imageUrl}`));

};

image.src = imageUrl;

});

});

Promise.all(promises)

.then((images) => {

const imageContainer = document.getElementById('image-container');

images.forEach((image) => {

const imgElement = document.createElement('img');

imgElement.src = image.src;

imgElement.alt = '预加载图片';

imageContainer.appendChild(imgElement);

});

})

.catch((error) => {

console.error('图片加载错误:', error);

});

在上面的示例中,我们通过map方法遍历所有的图片链接,创建了一系列Promise对象并添加到了promises数组中。然后使用Promise.all方法等待这些Promise对象都变为成功状态或失败状态时,才会执行后面的then方法或catch方法。在then方法中,我们通过forEach方法遍历所有的图片,创建一个新的img元素,并将其添加到页面中。

3.2 获取异步操作的结果

下面是一个简单的实现一个异步操作得到结果的示例,该示例使用Promise对象的then方法处理异步操作成功的结果。

function fetchData(url) {

return new Promise((resolve, reject) => {

const xhr = new XMLHttpRequest();

xhr.open('GET', url, true);

xhr.onreadystatechange = function() {

if (xhr.readyState === 4) {

if (xhr.status === 200) {

resolve(xhr.responseText);

} else {

reject(new Error(`请求失败 ${xhr.status}`));

}

}

};

xhr.send();

});

}

fetchData('http://example.com/data.json')

.then((data) => {

console.log('data:', data);

})

.catch((error) => {

console.error('请求错误:', error);

});

在上面的示例中,我们通过XMLHttpRequest对象获取一个JSON文件,并返回一个Promise对象。然后通过then方法处理Promise对象成功状态下的返回值,并输出到控制台中;通过catch方法处理Promise对象失败状态下的错误信息,并输出到控制台中。

3.3 模拟ajax请求

下面是一个简单的实现模拟ajax请求的示例,该示例使用Promise对象的resolve方法和setTimeout函数模拟异步地向服务器发送请求和获取结果。

function ajax(url, options) {

return new Promise((resolve, reject) => {

const xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {

if (xhr.readyState === 4) {

if (xhr.status === 200) {

setTimeout(() => {

resolve(xhr.responseText);

}, options.timeout || 0);

} else {

reject(new Error(`请求失败 ${xhr.status}`));

}

}

};

xhr.open(options.method || 'GET', url, true);

for (const key in options.headers) {

if (options.headers.hasOwnProperty(key)) {

xhr.setRequestHeader(key, options.headers[key]);

}

}

xhr.send(options.body || null);

});

}

ajax('http://example.com/data.json', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify({name: 'jerry', age: 18}),

timeout: 1000

})

.then((result) => {

console.log('请求结果:', result);

})

.catch((error) => {

console.error('请求失败:', error);

});

在上面的示例中,我们通过XMLHttpRequest对象向服务器发送一个POST请求,并返回一个Promise对象。然后通过resolve方法模拟异步返回结果,并在then方法中处理成功状态下的结果;通过reject方法返回错误信息,并在catch方法中处理失败状态下的错误信息。