JavaScript Promise

JavaScript Promise

Promise基础概念Promise 是一个代表异步操作最终完成或失败的对象。它有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)const promise = new Promise((resolve, reject) => { // 异步操作...

2024年12月30日
1.4千字

Promise基础概念

Promise 是一个代表异步操作最终完成或失败的对象。它有三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)
const promise = new Promise((resolve, reject) => {
    // 异步操作
    if (/* 操作成功 */) {
        resolve(value);
    } else {
        reject(error);
    }
});

Promise 的基本用法

// 基本使用
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("成功");
    }, 1000);
});

promise
    .then(result => console.log(result))     // 处理成功
    .catch(error => console.log(error))      // 处理错误
    .finally(() => console.log("结束"));     // 总是执行

Promise实例方法

then catch finally

Promise.reject(123)
  .then(res => console.info('then: ', res))
  .catch(error => console.info('catch: ', error))
  .finally(() => console.info('finally.'));

Promise静态方法

静态方法返回的都是Promise实例对象和new Promise一样

  • Promise.all 适合所有请求都必须成功的场景
  • Promise.allSettled 允许部分失败,适合容忍部分请求出错的需求
  • Promise.race 则常用于超时等情况
  • Promise.all 存在错误即退出

all

成功的情况

Promise.all([
  Promise.resolve(1),
  new Promise(resolve => {
    setTimeout(() => {
      resolve(2)
    }, 2000)
  }),
  Promise.resolve(3),
]).then(values => console.log(values));

失败的情况

console.group('>>> Promise start <<<')
Promise.all([
    Promise.resolve(1),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(2)
        }, 2000)
    }),
    Promise.resolve(3),
]).then(values => console.log(values)).catch(error => console.error(error));
console.groupEnd()


一些使用场景

init: {
  async all() {
    const initPromises = [];
    for (let fn in this) {
      if (fn !== 'all') {
        const result = this[fn]();
        if (result instanceof Promise) {
          initPromises.push(result);
        }
      }
    }
    await Promise.all(initPromises);
    $('#submit').click();
  }
  ···
}

这种情况下,init下面如果有其他异步方法值的属性,如果all函数不改造为Promise.all的方式,按钮click会有可能执行不了好的,我来为您详细讲解 Promise 的相关知识

allSettled

等待所有都执行完,即便存在失败的情况

/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0

THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */


/// <reference no-default-lib="true"/>

interface PromiseFulfilledResult<T> {
    status: "fulfilled";
    value: T;
}

interface PromiseRejectedResult {
    status: "rejected";
    reason: any;
}

type PromiseSettledResult<T> = PromiseFulfilledResult<T> | PromiseRejectedResult;

interface PromiseConstructor {
    /**
     * Creates a Promise that is resolved with an array of results when all
     * of the provided Promises resolve or reject.
     * @param values An array of Promises.
     * @returns A new Promise.
     */
    allSettled<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>; }>;

    /**
     * Creates a Promise that is resolved with an array of results when all
     * of the provided Promises resolve or reject.
     * @param values An array of Promises.
     * @returns A new Promise.
     */
    allSettled<T>(values: Iterable<T | PromiseLike<T>>): Promise<PromiseSettledResult<Awaited<T>>[]>;
}
console.group('>>> Promise.allSettled >>>')
Promise.allSettled([
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(2)
    }, 2000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(1)
    }, 1000)
  }),
]).then(values => console.log(values)).catch(error => console.error(error));
console.groupEnd()

race

console.group('>>> Promise <<<')
Promise.race([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2)
        }, 2000)
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000)
    }),
]).then(values => console.log(values)).catch(error => console.error(error));
console.groupEnd()
如果存在rejected状态的Promise

即便竟对结果是rejected,但是他不是首位跳出,也不会执行

Promise.race([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(2)
        }, 2000)
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 1000)
    }),
]).then(values => console.log(values)).catch(error => console.error(error));
console.groupEnd()

也和下面截图结果一样

any

存在错误即退出

console.group('>>> Promise.any >>>')
Promise.any([
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(2)
    }, 2000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    }, 1000)
  }),
]).then(values => console.log(values)).catch(error => console.error(error));
console.groupEnd()

resolve

Promise.resolve(123).then(res => console.info('then: ', res)).catch(error => console.info('catch: ', error));

reject

Promise.reject(123).then(res => console.info('then: ', res)).catch(error => console.info('catch: ', error));

try

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/try

withResolvers

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers

async/await 语法糖

// async/await 使异步代码更易读
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('获取数据失败:', error);
    }
}
const asyncFun = async () => {
  try {
    await Promise.resolve(123);
    return 333;
  } catch (error) {
    return Promise.reject(error);
  } finally {
    console.info('finally');
  }
}
console.info('asyncFun instance: ', asyncFun())

错误处理

// 错误处理方式
async function handleErrors() {
    try {
        await Promise.reject('出错了');
    } catch (error) {
        console.log('捕获到错误:', error);
    }
}

// 或者使用 .catch()
new Promise((resolve, reject) => {
    reject('出错了');
})
.catch(error => {
    console.log('捕获到错误:', error);
});

实际应用示例

// 实际应用场景
async function loadUserData() {
    try {
        // 并行请求
        const [userInfo, userPosts] = await Promise.all([
            fetch('/api/user'),
            fetch('/api/posts')
        ]);
        
        // 串行处理结果
        const userData = await userInfo.json();
        const postsData = await userPosts.json();
        
        return {
            user: userData,
            posts: postsData
        };
    } catch (error) {
        console.error('加载用户数据失败:', error);
        throw error;
    }
}
const utils = {
  /**
     * 获取看板指定接口
     * 获取PC033012配置,根据配置决定是否返回新接口
     * @param {String} oldUrl 旧接口url
     * @param {String} newUrl 新接口url
     * @returns {Promise<String>}
     */
  getBoardUrlWithSetting(oldUrl, newUrl) {
    if (boardUtils.boardUrlSettingCacheValue !== null) {
      console.info('读PC033012配置缓存值: ', boardUtils.boardUrlSettingCacheValue);
      return Promise.resolve(boardUtils.boardUrlSettingCacheValue === '1' ? newUrl : oldUrl);
    }

    if (boardUtils._boardUrlSettingPromise) {
      return boardUtils._boardUrlSettingPromise
        .then(() => {
          console.info('等待获取PC033012配置缓存值: ', boardUtils.boardUrlSettingCacheValue);
          return boardUtils.boardUrlSettingCacheValue === '1' ? newUrl : oldUrl;  // 直接返回 URL 字符串
        })
        .fail(error => {
          console.error('[MRP] getBoardUrlWithSetting error:', error);
          boardUtils._boardUrlSettingPromise = null;
          return oldUrl;
        });
    }

    boardUtils._boardUrlSettingPromise = boardUtils.getBoardMrpConfig('PC033012')
      .then(config => {
        if (config && config.data && config.data.value) {
          const value = config.data.value;
          boardUtils.boardUrlSettingCacheValue = value;
          console.info('首次获取PC033012配置值: ', value);
          boardUtils._boardUrlSettingPromise = null;
          return Promise.resolve(value === '1' ? newUrl : oldUrl);
        } else {
          return Promise.resolve(oldUrl);
        }
      })
      .fail(error => {
        console.error('[MRP] getBoardUrlWithSetting error:', error);
        boardUtils._boardUrlSettingPromise = null;
        return oldUrl;
      });

    return boardUtils._boardUrlSettingPromise;
  },
  /**
     * 是否使用看板新接口 for 有的看板新接口请求类型也变了
     * @returns {String} '0' 使用旧接口 '1' 使用新接口
     */
  getBoardNewUrlFlag() {
    return boardUtils.getBoardUrlWithSetting('0', '1');
  },
  _boardUrlSettingPromise: null,
  boardUrlSettingCacheValue: null,
}

重要提示

  1. Promise 状态一旦改变就不能再变
  2. .then().catch() 总是返回新的 Promise
  3. 异步操作应该优先考虑使用 Promise 而不是回调
  4. async/await 让异步代码更容易理解和维护
  5. 记得始终处理 Promise 的错误情况

这些是 Promise 的核心概念和使用方法,掌握这些可以帮助您更好地处理 JavaScript 中的异步操作。

文章评论区

欢迎留言交流

未登录,请先注册或登录后发表评论。

Leave comment