理解Promise in JavaScript

2023-01-01  本文已影响0人  videoisfun

Promise是JavaScript中的一个核心概念,初学JavaScript,对Promise的概念和用法都比较模糊,这里做一个总结。
首先Promise是用来执行异步代码的,用来替代回调函数。在讲回调函数之前,可以先看下什么是同步操作,什么是异步操作。

同步操作

正常代码是同步执行的,执行一条命令需要等它完成之后才会执行下一条命令。例如读取一个文件的内容,

// Node.js program to demonstrate the
// fs.readFileSync() method

// Include fs module
const fs = require('fs');

// Calling the readFileSync() method
// to read 'input.txt' file
const data = fs.readFileSync('./input.txt',
            {encoding:'utf8', flag:'r'});

// Display the file data
console.log(data);

执行完之后立刻就可以知道文件的内容,但这种方式耗时比较久,在等待执行完成的过程时候,不能执行其他操作,这样就很容易阻塞到UI线程。

异步操作

与同步方式不同,异步操作在调用某个函数后立刻返回,同时使用callback函数,在异步操作完成的时候自动调用。读文件用异步来操作可以表示为:

readFile("./input.txt", (error, result) => {
  // This callback will be called when the task is done, with the
  // final `error` or `result`. Any operation dependent on the
  // result must be defined within this callback.
  console.log(result);
});
// Code here is immediately executed after the `readFile` request
// is fired. It does not wait for the callback to be called, hence
// making `readFile` "asynchronous".

这种异步操作有两个缺点,一个是多层的callback会比较晦涩难懂,不容易维护;第二个是出错处理很麻烦,因为实际在执行回调函数的时候已经没有之前同步执行的堆栈,没有办法回溯到可以处理异常的代码块。

Promise

Promise用来表示一次异步操作的未来结果,既然是表示未来的结果,那就需要通过这个Promise可以知道异步操作是否成功,如果成功,返回值是什么?如果失败,失败的异常是什么。同时因为是异步操作,Promise可以注册回调函数,需要注意的是,注册的回调函数最多只能被调用一次;

创建Promise

  1. 使用Promise的构造函数来创建,用法如下:
    创建一个Promise的语法是
new Promise(executor)

其中executor是一个函数,接受两个参数resolverejectresolvereject都是函数,并且接受一个任意类型的输入参数。
其中resolve函数解决或兑现返回的Promise,或者调用reject函数拒绝返回的Promise.
上面这个例子中的执行过程如下:

  1. new Promise会构造一个Promise对象,同时会产生两个函数对象,分别是resolvereject,这两个函数对象和这个Promise对象绑定在一起。
  2. new Promise的参数是一个函数executor,这个函数用来封装一些操作,这些操作会通过异步的方式执行。同时这个函数接受两参数,分别是resolvereject. executor在创建Promise后立刻执行,同时把resolvereject的对象作为参数。
  3. 这个Promise的状态是通过调用resolvereject的调用来改变的。
const myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code.
  // In reality, you will probably be using something like XHR or an HTML API.
  setTimeout(() => {
    resolve("Success!"); // Yay! Everything went well!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log(`Yay! ${successMessage}`);
});

以上例子中就是通过resolve来把"Success"信息传给通过myFirstPromise.then注册的回调函数,同时myFirstPromise也会变为fulfill的状态。

  1. 使用then()函数来创建
    then()可以创建并返回一个新的Promise,例如
function getJSON(url){
  return fetch(url).then(response => response.json());
}

fetch(url)返回是一个Promise P1P1对应的实际结果是一个Response对象。Response对象的 json()方法返回一个新的Promise P2,那么P1被解决为P2。当P2兑现时(fulfill),P1也会用相同的值来fulfill.
还是用刚才读文件的例子来说明,如果用Promise实现的话就是以下方式:

const readFilePromise = (path) =>
  new Promise((resolve, reject) => {
    readFile(path, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });

readFilePromise("./input.txt")
  .then((result) => console.log(result))
  .catch((error) => console.error("Failed to read data"));

使用Promise

如果使用Promise异步,假设有一个函数readFilePromise,那么上面的代码就变成:

readFilePromise("./input.txt").then(fileContent => {
  // 该函数为callback函数,在文件内容被读出的时候调用,接受的参数为
  // 文件的内容。注意该函数只会被调用一次。
  console.log(fileContent);
});

注意readFilePromise返回的是一个Promise对象,Promise对象有then()的方法,可以用来注册回调函数。该回调函数在Promise兑现后调用,调用的时候传入的参数为该Promise的实际结果,在此处就是文件的实际内容;
如果读取文件失败,怎么处理:

readFilePromise("./input.txt").then(fileContent => {
  // 该函数为callback函数,在文件内容被读出的时候调用,接受的参数为
  // 文件的内容。注意该函数只会被调用一次。
  console.log(fileContent);
}).catch(() => console.log("error"));

如果要等待Promise的完成,可以使用

await yourPromise;

Promise的状态

一个Promise可以有三个状态,分别是fulfillrejectpending。刚创立的时候,Promise的状态为pending,一旦被fulfill或者reject,则该Promise为settle,且状态不会改变。
一个Promise代表了异步操作的结果,如果异步代码正常结束,这个结果就是代码的正常返回值,同时这个结果会作为参数传递给then()的第一个参数注册的函数;如果代码执行异常,那这个结果就是一个Error对象或者某个其他值,这个结果会作为参数传递给catch()注册的或者then()的第二个参数注册的回调函数。

Promise Chain

先看一个例子:

fetch(theURL)                // task 1, return Promise P1
    .then(callback1)         // task 2, return Promise P2
    .then(callback2);        // task 3, return Promise P3

callback1是当P1兑现的时候调用,并且把P1的结果作为参数传给callback1; callback1必须返回一个新的Promise P2,并把P2兑现的结果作为输入参数送给callback2
举一个具体的例子:

function callback1(response){
  let p4 = response.json();
  return p4
}

function callback2(profile){
  displayUserProfile(profile);
}
let p1 = fetch("/api/user/profile");
let p2 = p1.then(callback1);
let p3 = p2.then(callback2);

Reference

MDN Promise
参数结构
JavaScript权威指南第7版

上一篇下一篇

猜你喜欢

热点阅读