JavaScript--明明白白Promise (Park One)

明明白白Promise (Park One)

Promise是一种用于处理异步操作的特殊对象。它代表了一个尚未完成但最终会完成的操作,并可以在操作完成后返回结果或错误。

Promise有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。当一个Promise被创建时,它处于pending状态。操作可以是异步的,当操作成功完成时,Promise将转为fulfilled状态,并提供相应的结果。相反,如果操作失败,则Promise将转为rejected状态,并提供错误的原因。

使用Promise,可以通过链式调用then()方法来处理异步操作的结果。then()方法接受两个回调函数作为参数:一个用于处理操作成功完成时的结果,另一个用于处理操作被拒绝时的错误。这种链式调用的方式使得代码更加清晰和易于维护。

除了then()方法,Promise还提供了其他方法,如catch()用于捕获错误,finally()用于指定无论Promise状态如何都要执行的操作。

Promise的出现使得JavaScript中的异步编程更加优雅和可读,避免了回调地狱(callback hell)的问题。它已经成为现代JavaScript中处理异步操作的一种标准方式。

Promise 在各种开源库中已经实现,现在标准化后被浏览器默认支持。

传统实现,代码会陷入回调地狱的例子:

javascript 复制代码
function getUser(userId, callback) {
  // 异步操作获取用户数据
  setTimeout(() => {
    const user = { id: userId, name: 'John' };
    callback(user);
  }, 1000);
}

function getPosts(user, callback) {
  // 异步操作获取用户的帖子数据
  setTimeout(() => {
    const posts = ['Post 1', 'Post 2', 'Post 3'];
    callback(posts);
  }, 1000);
}

function getComments(post, callback) {
  // 异步操作获取帖子的评论数据
  setTimeout(() => {
    const comments = ['Comment 1', 'Comment 2', 'Comment 3'];
    callback(comments);
  }, 1000);
}

getUser(1, (user) => {
  getPosts(user, (posts) => {
    getComments(posts[0], (comments) => {
      console.log(comments);
    });
  });
});

我们需要获取用户数据、帖子数据和评论数据。由于这些操作都是异步的,我们使用了多个嵌套的回调函数来处理它们。这样的代码结构难以阅读和维护,尤其是在处理更复杂的异步操作时。

Promise 的优点之一是可以通过链式调用来避免回调地狱问题,使代码结构更加清晰和可读。一个 promise 必须有一个 then 方法用于处理状态改变

Promise 包含pendingfulfilledrejected三种状态

  • pending 指初始等待状态,初始化 promise 时的状态
  • resolve 指已经解决,将 promise 状态设置为fulfilled
  • reject 指拒绝处理,将 promise 状态设置为rejected
  • promise 是生产者,通过 resolvereject 函数告之结果
  • promise 非常适合需要一定执行时间的异步任务

状态一旦改变将不可更改,promise 没有使用 resolvereject 更改状态时,状态为 pending

javascript 复制代码
console.log(
  new Promise((resolve, reject) => {
  });
); //Promise {<pending>}

当更改状态后

javascript 复制代码
console.log(
  new Promise((resolve, reject) => {
    resolve("fulfilled");
  })
); //Promise {<resolved>: "fulfilled"}

console.log(
  new Promise((resolve, reject) => {
    reject("rejected");
  })
); //Promise {<rejected>: "rejected"}

promise 创建时即立即执行即同步任务,then 会放在异步微任务中执行,需要等同步任务执行后才执行。

javascript 复制代码
let promise = new Promise((resolve, reject) => {
  resolve("fulfilled");
  console.log("success");
});
promise.then(msg => {
  console.log(msg);
});
console.log("ss-s.cc");
promise` 操作都是在其他代码后执行,下面会先输出 `ss-s.cc` 再弹出 `success

promise 的 then、catch、finally 的方法都是异步任务

程序需要将主任务执行完成才会执行异步队列任务

javascript 复制代码
const promise = new Promise(resolve => resolve("success"));
promise.then(alert);
alert("ss-s.cc");
promise.then(() => {
  alert("success");
});

下例在三秒后将 Promise 状态设置为 fulfilled ,然后执行 then 方法

javascript 复制代码
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("fulfilled");
  }, 3000);
}).then(
  msg => {
    console.log(msg);
  },
  error => {
    console.log(error);
  }
);

状态被改变后就不能再修改了,下面先通过resolve 改变为成功状态,表示promise 状态已经完成,就不能使用 reject 更改状态了

javascript 复制代码
new Promise((resolve, reject) => {
  resolve("操作成功");
  reject(new Error("请求失败"));
}).then(
  msg => {
    console.log(msg);
  },
  error => {
    console.log(error);
  }
);

动态改变

如果你想在没有使用 Promise 的情况下动态改变异步操作的处理方式,一个可能的方法是使用回调函数作为参数,并在需要改变处理方式时更改回调函数。

javascript 复制代码
function asyncOperation(callback) {
  setTimeout(() => {
    const data = 'Async data';
    callback(data);
  }, 1000);
}

// 初始情况下使用默认的回调函数处理异步操作结果
asyncOperation((result) => {
  console.log('默认处理方式:', result);
});

// 动态改变处理方式,使用新的回调函数处理异步操作结果
const newCallback = (result) => {
  console.log('新的处理方式:', result.toUpperCase());
};

asyncOperation(newCallback);

我们使用默认的回调函数来处理异步操作结果。然后,我们定义了一个新的回调函数 newCallback,它会将结果转换为大写形式。通过将新的回调函数传递给 asyncOperation 函数,我们可以动态地改变处理方式。

使用Promise可以更方便地实现动态改变异步操作的处理方式。

javascript 复制代码
function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = 'Async data';
      resolve(data);
    }, 1000);
  });
}

// 初始情况下使用默认的处理方式处理异步操作结果
asyncOperation()
  .then((result) => {
    console.log('默认处理方式:', result);
  });

// 动态改变处理方式,使用新的处理方式处理异步操作结果
const newHandler = (result) => {
  console.log('新的处理方式:', result.toUpperCase());
};

// 创建一个新的 Promise,并在其上添加新的处理方式
const newPromise = asyncOperation()
  .then(newHandler);

// 在新的 Promise 上继续添加其他处理方式
newPromise
  .then((result) => {
    console.log('额外的处理方式:', result.length);
  });

then的介绍及使用方法

什么是then?then() 是 Promise 对象上的方法之一,用于处理 Promise 的成功状态(fulfilled)。它接受两个参数:一个是处理成功状态的回调函数,另一个是处理失败状态(rejected)的回调函数(可选)。

语法:

javascript 复制代码
promise.then(onFulfilled, onRejected)
  • onFulfilled 是一个函数,它会在 Promise 对象被成功解决时调用,并接收解决(fulfilled)状态的值作为参数。这个函数可以处理异步操作的结果,执行其他操作,或者返回一个新的值或 Promise 对象。
  • onRejected 是一个可选的函数,它会在 Promise 对象被拒绝(rejected)时调用,并接收拒绝状态的原因作为参数。这个函数可以处理错误,进行错误处理或返回一个新的错误。

.then() 方法返回一个新的 Promise 对象,可以通过链式调用多个 .then() 方法来串联处理多个异步操作的结果。

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const randomNum = Math.random();
    if (randomNum < 0.5) {
      resolve(randomNum); // 操作成功完成,返回结果
    } else {
      reject(new Error('操作失败')); // 操作被拒绝,返回错误
    }
  }, 1000);
});

promise.then(
  (result) => {
    console.log('操作成功:', result);
    return result * 2; // 返回一个新的值
  },
  (error) => {
    console.log('操作失败:', error);
    throw new Error('新的错误'); // 抛出一个新的错误
  }
).then(
  (newResult) => {
    console.log('新的操作成功:', newResult);
  },
  (newError) => {
    console.log('新的操作失败:', newError);
  }
);

then的链式调用:

可以在一个 Promise 对象上多次调用 .then() 方法,以便依次处理异步操作的结果。每个 .then() 方法都返回一个新的 Promise 对象,使得你可以在后续的 .then() 方法中继续处理结果,形成一个链式的处理流程。

javascript 复制代码
function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = 'Async data';
      resolve(data);
    }, 1000);
  });
}

asyncOperation()
  .then((result) => {
    console.log('第一个处理步骤:', result);
    return result.toUpperCase();
  })
  .then((result) => {
    console.log('第二个处理步骤:', result);
    return result.split(' ');
  })
  .then((result) => {
    console.log('第三个处理步骤:', result);
    return result.reverse();
  })
  .then((result) => {
    console.log('最终结果:', result);
  })
  .catch((error) => {
    console.log('发生错误:', error);
  });

注意点:

  1. 返回值传递:.then() 方法可以返回一个值或一个新的 Promise 对象。如果回调函数返回一个值,该值将被包装在一个已解决(fulfilled)状态的 Promise 对象中,并作为下一个 .then() 方法的输入。如果回调函数返回一个 Promise 对象,下一个 .then() 方法将等待该 Promise 对象解决,并将其解决值作为输入。

  2. 异步操作的顺序:.then() 方法是异步的,它们会在 Promise 的解决(fulfilled)状态时被调用,而不会阻塞后续代码的执行。因此,在链式调用中的每个 .then() 方法中的代码可能会在其他 .then() 方法之前或之后执行。确保你理解和处理好异步操作的顺序和依赖关系。

  3. 错误处理:.then() 方法链中的任何一个步骤发生错误时,错误会被传递到后续的 .catch() 方法或链中的下一个 .then() 方法的错误处理回调函数中。使用 .catch() 方法来捕获和处理链式调用中的错误,确保错误被适当地处理和传递。

  4. 异常处理:如果在 .then() 方法中的回调函数中抛出异常(使用 throw 语句),异常会被自动捕获并传递给后续的 .catch() 方法或链中的下一个 .then() 方法的错误处理回调函数中。

  5. 链式调用的返回值:.then() 方法返回一个新的 Promise 对象,它代表了当前 .then() 方法的处理结果。这使得你可以通过链式调用多个 .then() 方法来形成一个处理流程。同时,返回的 Promise 对象可以用于后续的 .then() 方法的调用或错误处理。

  6. 注意 Promise 链的结束:在 .then() 方法链中,确保在链的最后使用 .catch() 方法或 .finally() 方法来处理最终的成功或失败状态,以避免未处理的 Promise 拒绝(rejected)状态导致的未捕获错误。

catch

Promise 对象的 .catch() 方法用于捕获和处理 Promise 链中发生的错误。当 Promise 链中的任何一个 Promise 被拒绝(rejected)时,错误会被传递到 .catch() 方法中进行处理。

.catch() 方法接受一个回调函数作为参数,该回调函数会在 Promise 被拒绝时被调用。回调函数可以接收一个参数,即拒绝的原因(通常是一个 Error 对象),并在该函数中执行错误处理逻辑。

javascript 复制代码
fetch('https://api.example.com/data')
  .then(response => {
    // 处理响应
    return response.json();
  })
  .then(data => {
    // 处理数据
    console.log(data);
  })
  .catch(error => {
    // 处理错误
    console.error('发生错误:', error);
  });

如果 fetch() 请求失败或者 .then() 中的任何一个操作抛出异常,错误将被传递到 .catch() 中进行处理。您可以在 .catch() 回调函数中执行适当的错误处理,例如打印错误消息或执行备选操作。

使用 .catch() 方法可以有效地捕获和处理 Promise 链中的错误,确保代码在出现异常情况时具有适当的容错和错误处理机制。

catch 用于失败状态的处理函数,等同于 then(null,reject){}

建议使用 catch 处理错误将 catch 放在最后面用于统一处理前面发生的错误

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  reject(new Error("Notice: Promise Exception"));
}).catch(msg => {
  console.error(msg);
});

catch 可以捕获之前所有 promise 的错误,所以建议将 catch 放在最后。下例中 catch 也可以捕获到了第一个 then 返回 的 promise 的错误。

javascript 复制代码
new Promise((resolve, reject) => {
  resolve();
})
.then(() => {
  return new Promise((resolve, reject) => {
    reject(".then ");
  });
})
.then(() => {})
.catch(msg => {
  console.log(msg);
});

错误是冒泡的操作的,下面没有任何一个then 定义第二个函数,将一直冒泡到 catch 处理错误

javascript 复制代码
new Promise((resolve, reject) => {
  reject(new Error("请求失败"));
})
.then(msg => {})
.then(msg => {})
.catch(error => {
  console.log(error);
});
catch 也可以捕获对 then 抛出的错误处理

new Promise((resolve, reject) => {
  resolve();
})
.then(msg => {
  throw new Error("这是then 抛出的错误");
})
.catch(() => {
  console.log("33");
});

也可以捕获其他错误,下面在 then 中使用了未定义的变量,将会把错误抛出到 catch

javascript 复制代码
new Promise((resolve, reject) => {
  resolve("success");
})
.then(msg => {
  console.log(a);
})
.catch(reason => {
  console.log(reason);
});

使用建议
建议将错误要交给catch处理而不是在then中完成,不建议使用下面的方式管理错误

javascript 复制代码
new Promise((resolve, reject) => {
  reject(new Error("请求失败"));
}).then(
  msg => {
    console.log(msg);
  },
  error => {
    console.log(error);
  }
);

处理机制,在 promise 中抛出的错误也会被catch 捕获

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  throw new Error("fail");
}).catch(msg => {
  console.log(msg.toString());
});

注意:可以将上面的理解为如下代码,可以理解为内部自动执行 try...catch

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  try {
    throw new Error("fail");
  } catch (error) {
    reject(error);
  }
}).catch(msg => {
  console.log(msg.toString());
});

但像下面的在异步中 throw 将不会触发 catch,而使用系统错误处理

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error("fail");
  }, 2000);
}).catch(msg => {
  console.log(msg);
});

下面在then 方法中使用了没有定义的hd函数,也会抛除到 catch 执行,可以理解为内部自动执行 try...catch

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  resolve();
})
.then(() => {
  hd();
})
.catch(msg => {
  console.log(msg.toString());
});
在 catch 中发生的错误也会抛给最近的错误处理

const promise = new Promise((resolve, reject) => {
  reject();
})
.catch(msg => {
  hd();
})
.then(null, error => {
  console.log(error);
});

fanlly的介绍与用法

Promise 对象的 .finally() 方法用于指定不论 Promise 是否成功完成或被拒绝,都要执行的逻辑。无论 Promise 的状态如何,.finally() 方法都会在 Promise 链中的最后执行。

.finally() 方法接受一个回调函数作为参数,该回调函数在 Promise 完成或被拒绝时被调用。它不接收任何参数,因此无法获取 Promise 的结果或拒绝原因,仅用于执行一些清理操作或在 Promise 完成后执行一些必要的逻辑。

javascript 复制代码
fetch('https://api.example.com/data')
  .then(response => {
    // 处理响应
    return response.json();
  })
  .then(data => {
    // 处理数据
    console.log(data);
  })
  .catch(error => {
    // 处理错误
    console.error('发生错误:', error);
  })
  .finally(() => {
    // 执行清理操作或其他逻辑
    console.log('完成处理');
  });

注意:无论状态是resolve 或 reject 都会执行此动作,finally 与状态无关。

图片加载的综合例子:

javascript 复制代码
function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();

    img.onload = function() {
      resolve(img);
    };

    img.onerror = function() {
      reject(new Error('图片加载失败'));
    };

    img.src = url;
  });
}

function displayImage(image) {
  const container = document.getElementById('image-container');
  container.appendChild(image);
}

function handleImageLoad(url) {
  loadImage(url)
    .then(image => {
      displayImage(image);
    })
    .catch(error => {
      console.error('加载图片出错:', error);
    })
    .finally(() => {
      console.log('清理工作完成');
    });
}

// 调用示例
const imageUrl = 'https://example.com/image.jpg';
handleImageLoad(imageUrl);

首先定义了 loadImage() 函数,它接受一个图片的URL作为参数,并返回一个 Promise 对象。在 Promise 的构造函数中,我们创建一个新的 Image 对象,并设置其 onload 和 onerror 事件处理程序。当图片加载成功时,我们调用 resolve() 方法,将加载的图片对象传递给 .then() 方法。当图片加载失败时,我们调用 reject() 方法,将一个错误对象传递给 .catch() 方法。

在 handleImageLoad() 函数中,我们调用 loadImage() 函数来加载图片,并使用 .then() 方法来处理加载成功的情况。在 .then() 方法的回调函数中,我们调用 displayImage() 函数来显示加载的图片。如果加载出现错误,我们使用 .catch() 方法来处理错误情况。无论加载成功还是失败,我们都使用 .finally() 方法来进行清理工作。

欢迎点赞,关注! 谢谢!

相关推荐
DaphneOdera179 分钟前
Git Bash 配置 zsh
开发语言·git·bash
Code侠客行16 分钟前
Scala语言的编程范式
开发语言·后端·golang
yqcoder18 分钟前
NPM 包管理问题汇总
前端·npm·node.js
程序菜鸟营24 分钟前
nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)
前端·npm·node.js
lozhyf35 分钟前
Go语言-学习一
开发语言·学习·golang
bsr198335 分钟前
前端路由的hash模式和history模式
前端·history·hash·路由模式
dujunqiu1 小时前
bash: ./xxx: No such file or directory
开发语言·bash
爱偷懒的程序源1 小时前
解决go.mod文件中replace不生效的问题
开发语言·golang
日月星宿~1 小时前
【JVM】调优
java·开发语言·jvm
2401_843785231 小时前
C语言 指针_野指针 指针运算
c语言·开发语言