美文网首页
理解Promise in JavaScript

理解Promise in JavaScript

作者: videoisfun | 来源:发表于2023-01-01 22:03 被阅读0次

    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的调用来改变的。
    • 如果resolve先被调用,那么Promise就被解决或者兑现,同时传给resovle的参数会被认为Promise对象对应的结果。
    • 如果reject先被调用,那么Promise则被拒绝,同时传给reject的参数也会被认为是Promise对象对应的异常。
      以下例子来源于MDN:
    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版

    相关文章

      网友评论

          本文标题:理解Promise in JavaScript

          本文链接:https://www.haomeiwen.com/subject/irypxdtx.html