【JavaScript】Promise

75 阅读4分钟

背景

在JavaScript中,Promise 是一个代表了异步操作最终完成或失败的对象。它允许你以同步的方式(即使用 .then().catch() 和 .finally() 等方法)来处理异步操作的结果。Promise 的出现极大地简化了异步编程的复杂性,特别是在处理多个异步操作时。

基本概念

  • 状态:一个 Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
  • 解决(Resolve) :当异步操作成功完成时,Promise 被解决(fulfilled),并将结果作为参数传递给 .then() 方法中的回调函数。
  • 拒绝(Reject) :当异步操作失败时,Promise 被拒绝(rejected),并将错误作为参数传递给 .catch() 方法中的回调函数。

创建 Promise

你可以使用 new Promise() 构造函数来创建一个新的 Promise 对象。

这个构造函数接受一个执行器(executor)函数作为参数,该函数本身又接受两个函数作为参数:resolve 和 reject

let promise = new Promise(function(resolve, reject) {  
  // 异步操作  
  if (/* 操作成功 */) {  
    resolve(value); // 将Promise的状态从pending变为fulfilled,并将value作为结果  
  } else {  
    reject(error); // 将Promise的状态从pending变为rejected,并将error作为原因  
  }  
});

使用 Promise

你可以使用 .then() 方法来处理 Promise 成功的情况,使用 .catch() 方法来处理失败的情况,还可以使用 .finally() 方法来指定无论 Promise 最终状态如何都会执行的操作。

1、基本使用

promise.then(function(value) {  
  // 处理成功的情况  
  console.log(value);  
}).catch(function(error) {  
  // 处理失败的情况  
  console.error(error);  
}).finally(function() {  
  // 无论成功或失败都会执行  
  console.log('Promise执行完成');  
});

2、Promise封装读取文件

(1)非Promise封装

//1. 引入 fs 模块
const fs = require('fs');

//2. 调用方法读取文件
fs.readFile('./resources/为学.md', (err, data)=>{
    //如果失败, 则抛出错误
    if(err) throw err;
    //如果没有出错, 则输出内容
    console.log(data.toString());
});

(2)Promise封装

const fs = require('fs');
const p = new Promise(function(resolve, reject){
    fs.readFile("./resources/为学.mda", (err, data)=>{
        //判断如果失败
        if(err) reject(err);
        //如果成功
        resolve(data);
    });
});

p.then(function(value){
    console.log(value.toString());
}, function(reason){
    console.log("读取失败!!");
});

3、PromiseAJAX封装

        const p = new Promise((resolve, reject) => {
    //1. 创建对象
    const xhr = new XMLHttpRequest();
    //2. 初始化
    xhr.open("GET", "https://api.apiopen.top/getJ");

    //3. 发送
    xhr.send();

    //4. 绑定事件, 处理响应结果
    xhr.onreadystatechange = function () {
        //判断
        if (xhr.readyState === 4) {
            //判断响应状态码 200-299
            if (xhr.status >= 200 && xhr.status < 300) {
                //表示成功
                resolve(xhr.response);
            } else {
                //如果失败
                reject(xhr.status);
            }
        }
    }
})

//指定回调
p.then(function(value){
    console.log(value);
}, function(reason){
    console.error(reason);
});

4、Promise的then方法

//创建 promise 对象
const p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('用户数据');
        // reject('出错啦');
    }, 1000)
});

(1) 非 promise 类型的属性中使用

const result = p.then(value => {
    console.log(value);
    return 'iloveyou';
}, reason=>{
    console.warn(reason);
});

(2) promise 类型的属性中使用

const result = p.then(value => {
    console.log(value);
    return new Promise((resolve, reject)=>{
         resolve('ok');
         reject('error');
    });
    // 抛出错误
    // throw new Error('出错啦!');
    throw '出错啦!';
}, reason=>{
    console.warn(reason);
});

(3)链式调用

//链式调用
p.then(value=>{

}).then(value=>{

});

5、catch方法

const p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        //设置 p 对象的状态为失败, 并设置失败的值
        reject("出错啦!");
    }, 1000)
});

//方法一
// p.then(function(value){}, function(reason){
//     console.error(reason);
// });

//方法二
p.catch(function(reason){
    console.warn(reason);
});

练习(解决回调地狱)

//基础用法
const fs = require("fs");
fs.readFile('./resources/为学.md', (err, data1)=>{
    fs.readFile('./resources/插秧诗.md', (err, data2)=>{
        fs.readFile('./resources/观书有感.md', (err, data3)=>{
            let result = data1 + '\r\n' +data2  +'\r\n'+ data3;
            console.log(result);
        });
    });
});
//使用promise
//使用 promise 实现
const fs = require("fs");
const p = new Promise((resolve, reject) => {
    fs.readFile("./resources/为学.md", (err, data) => {
        resolve(data);
    });
});

p.then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/插秧诗.md", (err, data) => {
            resolve([value, data]);
        });
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/观书有感.md", (err, data) => {
            //压入
            value.push(data);
            resolve(value);
        });
    })
}).then(value => {
    console.log(value.join('\r\n'));
});

链式调用

Promise 的一个强大特性是它们支持链式调用。这意味着你可以在一个 .then() 方法的回调函数中返回另一个 Promise,然后继续在链上调用 .then().catch() 和 .finally()

someAsyncFunction()  
  .then(result => {  
    // 处理结果  
    return anotherAsyncFunction(result); // 返回另一个Promise  
  })  
  .then(newResult => {  
    // 处理新的结果  
  })  
  .catch(error => {  
    // 处理错误  
  })  
  .finally(() => {  
    // 清理工作  
  });

静态方法

Promise 还提供了几个静态方法,如 Promise.all()Promise.race() 和 Promise.resolve() 等,它们提供了处理多个 Promise 的能力或创建已解决/已拒绝的 Promise 的能力。

注意事项

  • 避免在 .then().catch() 或 .finally() 的回调函数中创建新的 Promise 时使用 new Promise() 构造函数,除非绝对必要。通常,你可以通过返回另一个异步操作的结果来保持链的连续性。
  • 总是处理 Promise 的错误,即使你确信操作不会失败。使用 .catch() 或在链的末尾添加一个空的 .catch() 来捕获可能未被处理的错误。
  • 考虑到 async/await 语法,它是基于 Promise 的,可以使得异步代码看起来和同步代码一样。如果可能的话,使用 async/await 来简化你的异步代码。