Skip to content

ES6 中 Promise

写在前面:经常用,但概念和使用经常现查现用,在这篇总结梳理一下

Promise 介绍

Promise 是异步编程的一种解决方案,可以替代传统的解决方案(回调函数和事件)。ES6 统一了用法,并原生提供了 Promise 对象。
具体区别如下:

  • 使用回调函数处理异步操作: 当异步操作完成时,回调函数会被调用。回调函数的主要问题是它们可以导致回调地狱(callback hell),也就是嵌套回调的情况
JavaScript
function getData(callback) {
  // 异步操作
  setTimeout(() => {
    const data = 'Hello, world!';
    callback(data);
  }, 1000);
}

getData((data) => {
  console.log(data); // 输出:Hello, world!
});
  • 使用 Promise 处理异步操作:
    Promise 的主要优点是它可以使异步代码更加清晰,避免回调地狱。Promise 还提供了链式调用的能力,这使得异步代码可以像同步代码一样顺序执行。
JavaScript
function getData() {
// 返回一个 Promise
return new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const data = 'Hello, world!';
    resolve(data);
  }, 1000);
});
}

getData().then((data) => {
  console.log(data); // 输出:Hello, world!
});
  • 回调地狱例子
JavaScript
//----回调地狱例子----//
doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('得到最终结果: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);
//使用Promise
doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);

Promise 的状态

作为对象 Promise 具有以下特点:

  • 对象的状态不受外界影响
  • 一旦状态改变了就不会再变,也就是说任何时候 Promise 都只有一种状态 Promise 对象仅有三种状态:
    • pending(进行中)
    • fulfilled(已成功)
    • rejected(已失败)

Promise 的用法

Promise 的构建

Promise 对象是一个构造函数,用来生成 Promise 实例

JavaScript
const promise = new Promise(function(resolve, reject) {});

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolvereject

  • resolve函数的作用是,将 Promise 的状态从 pending(进行中)更改为 fulfilled(已成功)。异步操作成功时,调用 resolve 函数,并将结果作为参数传递。
    示例:
JavaScript
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
  resolve('Operation successful');
}, 1000);
});
  • reject函数的作用是,将 Promise 的状态从 pending(进行中)更改为 rejected(已失败)。异步操作失败时,调用 reject 函数,并将错误作为参数传递
    示例:
JavaScript
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    reject(new Error('Operation failed'));
  }, 1000);
  });

Promise 的实例方法

Promise 构建出来的实例存在以下方法:

  • then()

    then 是实例状态发生改变时的回调函数,第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数
    then 方法返回的是一个新的 Promise 实例,可以根据这一特性,不断的链式调用回调函数 代码例子:

    JavaScript
    function greet () {
    var promise = new Promise (function(resolve, reject){
      var greet = "hello world"
      resolve(greet)
    })
    return promise
    }
    var p = greet().then(v => {
    console.log(v + '1') // 打印 hello world1
    return v
    })
    .then (v => {
      console.log(v + '2')// 打印 hello world2
    })
    .then (v => {
      console.log(v + '3')// 前面没有return v,此处 打印 undefined3
    })
    console.log(p)  // Promise { <pending> }
  • catch()

    当 Promise 的状态变为 rejected,catch 方法中的回调函数会被调用。这个回调函数接受一个参数,reject函数传递的错误。 注:Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止 代码例子:

    JavaScript
    const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
      reject(new Error('Operation failed'));
    }, 1000);
    });
    promise
      .then((result) => {
        console.log(result); // 如果操作成功,这行代码不会执行
      })
      .catch((error) => {
        console.error(error); // 如果操作失败,打印错误信息
    });
    //----“冒泡”性质----//
    doSomething()
    .then(result => doSomethingElse(result))
    .then(newResult => doThirdThing(newResult))
    .catch(error => console.error(error)); // 捕获并处理任何在上面的 Promise 链中出现的错误
  • finally()

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
    finally() 的回调函数不接受任何参数,这意味着你无法知道 Promise 的最终状态。 代码例子:

    JavaScript
    const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
      const success = Math.random() > 0.5; // 随机成功或失败
    
      if (success) {
        resolve('Operation successful');
      } else {
        reject(new Error('Operation failed'));
      }
    }, 1000);
    });
    
    promise
      .then((result) => {
        console.log(result); // 如果操作成功,打印 "Operation successful"
      })
      .catch((error) => {
        console.error(error); // 如果操作失败,打印错误信息
      })
      .finally(() => {
        console.log('Operation complete'); // 无论成功失败,都打印 "Operation complete"
      });

其他构造函数方法

Promise构造函数存在以下方法:

  • all()
    Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例 在以下两种情况下改变状态:

    1. 当所有的Promise实例都变为 fulfilled 状态时,Promise.all() 返回的 Promise 实例变为 fulfilled 状态,它的结果是一个数组,包含所有 Promise 实例的结果
    2. 如果参数中的任何一个 Promise 实例变为rejected状态,Promise.all() 返回的Promise实例就会立刻变为rejected 状态,它的结果是第一个rejectPromise 实例的结果。

注:如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法

  • 代码示例:
JavaScript
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});

       Promise.all([promise1, promise2, promise3])
         .then((values) => {
           console.log(values); // 输出:[3, 42, "foo"]
         })
         .catch((error) => {
           console.error(error);
         });
  • 代码实现
JavaScript
       //代码实现
       //使用 JavaScript 的 Promise 和 Array.prototype.reduce 方法
       function promiseAll(promises) {
        return new Promise((resolve, reject) => {
          //检查输入是否为数组
          if (!Array.isArray(promises)) {
            return reject(new TypeError('Arguments must be an array'));
          }
          //我们创建一个计数器和一个数组来跟踪已解决的 Promise
          let resolvedCounter = 0;
          let promiseNum = promises.length;
          let resolvedValues = new Array(promiseNum);
          //使用 Promise.resolve 来确保它是一个 Promise
          //并添加一个 then 处理程序
          // Promise 解决时,更新计数器和解决值数组。
          for (let i = 0; i < promiseNum; i++) {
            Promise.resolve(promises[i]).then((value) => {
              resolvedCounter++;
              resolvedValues[i] = value;
              if (resolvedCounter === promiseNum) {
                return resolve(resolvedValues);
              }
            }, (reason) => {
              // 如果任何一个 promise 被 reject,则整个 Promise.all 被 reject
              return reject(reason);
            });
          }
        });
      }
  • race()

    Promise.race() 会在数组中的任何一个 Promise 实例率先改变状态时,立即将自己的状态改变为相同的状态,并将那个率先改变的 Promise 实例的返回值,作为自己的返回值

  • 代码示例:

JavaScript
  const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
  });

  const promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'two');
  });

  Promise.race([promise1, promise2])
    .then((value) => {
      console.log(value); // 输出:"two"
    })
    .catch((error) => {
      console.error(error);
    });
  • allSettled() Promise.allSettled()方法接受一组 Promise实例作为参数,包装成一个新的 Promise实例 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

  • 代码示例

    JavaScript
    const promise1 = Promise.resolve(3);
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(reject, 100, 'foo');
    });
    
    Promise.allSettled([promise1, promise2])
      .then((results) => {
        console.log(results);
      });

使用场景

  1. 将图片的加载写成一个 Promise,一旦加载完成,Promise 的状态就发生变化
JavaScript
 const preloadImage = function (path) {
   return new Promise(function (resolve, reject) {
       const image = new Image();
       image.onload  = resolve;
       image.onerror = reject;
       image.src = path;
     });
  };
  1. 通过链式操作,将多个渲染数据分别给个 then,或当下个异步请求依赖上个请求结果的时候,通过链式操作友好解决问题
JavaScript
   // 各司其职
   getInfo().then(res=>{
       let { bannerList } = res
       //渲染轮播图
       console.log(bannerList)
       return res
   }).then(res=>{

       let { storeList } = res
       //渲染店铺列表
       console.log(storeList)
       return res
   }).then(res=>{
       let { categoryList } = res
       console.log(categoryList)
       //渲染分类列表
       return res
   })
  1. 通过 all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个 loading 即可
JavaScript
 function initLoad(){
     // loading.show() //加载loading
     Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
         console.log(res)
         loading.hide() //关闭loading
     }).catch(err=>{
         console.log(err)
         loading.hide()//关闭loading
     })
 }
 //数据初始化
 initLoad()
  1. 通过 race 可以设置图片请求超时
JavaScript
  let imgLoad = new Promise(function(resolve, reject) {
  let img = new Image();
  img.onload = resolve;
  img.onerror = reject;
  img.src = 'http://example.com/my-image.png';
  });

  let timeout = new Promise(function(resolve, reject) {
    setTimeout(reject, 5000, 'Image load timed out');
  });

  Promise.race([imgLoad, timeout])
    .then(function() {
      console.log('Image loaded successfully');
    })
    .catch(function(error) {
      console.log(error);
    });

最后更新于:

如有转载或 CV 的请标注本站原文地址