当前位置: 首页 > article >正文

一篇文章学会ES6 Promise

ES6 Promise详解

一、JavaScript中实现异步的方式总结

JavaScript 的异步操作实现机制主要包括以下几种技术和模式:

  1. 回调函数 (Callbacks)
  2. Promises
  3. async/await

下面是对每种技术的详细解释:

1. 回调函数 (Callbacks)

回调函数是最基本的异步处理机制。你将一个函数作为参数传递给另一个函数,并在异步操作完成时调用这个回调函数。这种方法虽然简单,但可能会导致所谓的“回调地狱”——即多个回调嵌套在一起,使得代码难以维护。

示例:

function fetchData(callback) {
  setTimeout(() => {
    const data = "Data fetched";
    callback(data);
  }, 1000);
}

fetchData((data) => {
  console.log(data); // 输出: Data fetched
});

2. Promises

Promise 是一种更现代的处理异步操作的方式,它提供了更好的链式调用和错误处理机制。Promise 代表一个可能在未来某个时间点完成(或失败)的操作的结果。

示例:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Data fetched";
      resolve(data);
    }, 1000);
  });
}

fetchData().then((data) => {
  console.log(data); // 输出: Data fetched
}).catch((error) => {
  console.error("Error:", error);
});

解释:

  • Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
  • resolve 用于将 Promise 的状态从 pending 转为 fulfilled
  • reject 用于将 Promise 的状态从 pending 转为 rejected
  • .then() 用于处理成功的结果,.catch() 用于处理错误。

3. async/await

async/await 是基于 Promises 的语法糖,使异步代码看起来像同步代码,从而提高代码的可读性和可维护性。

示例:

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = "Data fetched";
      resolve(data);
    }, 1000);
  });
}

async function getData() {
  try {
    const data = await fetchData();
    console.log(data); // 输出: Data fetched
  } catch (error) {
    console.error("Error:", error);
  }
}

getData();

解释:

  • async 函数总是返回一个 Promise
  • await 用于等待 Promise 完成,await 表达式可以在 async 函数内部使用。
  • 使用 try/catch 语句处理 await 表达式可能抛出的错误。

事件循环 (Event Loop)

JavaScript 是单线程的,所有的异步操作都是通过事件循环机制实现的。事件循环允许 JavaScript 处理异步操作,同时保持主线程的顺畅执行。基本的事件循环工作流程如下:

  1. 执行同步代码(从执行栈中)。
  2. 将异步操作(如 setTimeoutPromise)注册到任务队列。
  3. 当执行栈为空时,事件循环从任务队列中取出任务,并执行它们。

示例:

console.log("Start");

setTimeout(() => {
  console.log("Timeout 1");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise 1");
});

console.log("End");

解释:

  1. 同步代码 console.log("Start")console.log("End") 先执行。
  2. setTimeoutPromise 的回调函数分别被加入到任务队列中。
  3. 当执行栈为空时,事件循环会处理任务队列中的回调函数。由于 Promise 回调的优先级高于 setTimeout,所以 Promise 1 会先于 Timeout 1 输出。

总结

JavaScript 通过回调函数、Promises 和 async/await 提供了不同的异步处理机制,这些机制都依赖于事件循环来协调异步操作的执行。这些工具和技术让你能够编写更为流畅的异步代码,从而有效地处理复杂的应用逻辑。

二、Promise基本使用介绍

当然!在 TypeScript 中,Promise 的使用可以涉及多个方面,包括基础用法、类型系统、异步操作的错误处理以及高级用法等。下面我会详细讲解这些内容。

1. Promise 的基本概念

Promise 是一种用于处理异步操作的对象。它代表了一个在未来可能完成的操作及其结果。基本的 Promise 状态有:

  • Pending(进行中): 初始状态,操作尚未完成。
  • Fulfilled(已成功): 操作完成,结果成功。
  • Rejected(已失败): 操作完成,结果失败。

2. 创建和使用 Promise

创建 Promise

一个 Promise 对象由一个执行器函数(executor function)初始化,该函数接受两个参数:resolvereject

const promise = new Promise<number>((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = true; // 假设这是某个条件的结果
    if (success) {
      resolve(42); // 成功,返回值为 42
    } else {
      reject(new Error('Something went wrong')); // 失败,返回错误
    }
  }, 1000);
});

使用 Promise

可以使用 thencatch 方法来处理 Promise 的结果或错误。

promise
  .then(result => {
    console.log('Result:', result); // Output: Result: 42
  })
  .catch(error => {
    console.error('Error:', error.message);
  });

3. TypeScript 的类型系统与 Promise

TypeScript 允许你指定 Promise 的类型,这样可以让你在编译时得到类型检查。

function fetchNumber(): Promise<number> {
  return new Promise<number>((resolve, reject) => {
    setTimeout(() => {
      resolve(123);
    }, 1000);
  });
}

在这个例子中,fetchNumber 函数返回一个 Promise<number>。这意味着当 Promise 完成时,它将返回一个 number 类型的结果。

4. asyncawait

asyncawait 是处理 Promise 的语法糖,使得异步代码看起来像是同步的。在JavaScript中,async和await是用于处理异步操作的关键字。它们通常一起使用,以简化异步代码的编写和读取。

async 函数

async 关键字用于定义一个异步函数,异步函数总是返回一个 Promise对象。即使你在 async 函数中返回一个普通值,它也会被包装成一个 Promise。在async函数内部,可以使用await关键字等待Promise的完成。

async function getNumber(): Promise<number> {
  return 123; // 实际上返回的是 Promise.resolve(123)
}
await 关键字

await 关键字只能在 async 定义的异步函数中使用。它会暂停 async 函数的执行,直到 Promise 被解决或拒绝。await表达式的值就是Promise resolve的结果。

async function main() {
  try {
    const number = await getNumber();
    console.log(number); // Output: 123
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

5. 异常处理

在异步代码中处理错误非常重要。你可以使用 try/catch 语句来捕获 async 函数中的错误,或者在 Promise 链中使用 catch 方法。

async function fetchData(): Promise<number> {
  return new Promise<number>((resolve, reject) => {
    // 模拟错误
    setTimeout(() => reject(new Error('Failed to fetch data')), 1000);
  });
}

async function main() {
  try {
    const data = await fetchData();
    console.log(data);
    // 这里的data是一个`Promise`对象
  } catch (error) {
    console.error('Error:', error.message); // Output: Error: Failed to fetch data
  }
}

main();

使用Promose链中的catch方法捕获错误信息

async function fetchData():Promise<number>{
    return new Promise<number>((resolve,reject) => {
        setTimeout(() => reject(new Error('Failed to fetch data')), 1000);
    })
}

async function main(){
    featchData().then(res=>{
        // 这里的`res`resolve中返回的参数
        console.log(res);
    }).catch((e)=>{
        console.log("error",e);
    })
}

6. Promise.all 和 Promise.race

有时你可能需要处理多个 PromisePromise.allPromise.race 是两个有用的方法。

  • Promise.all: 接受一个 Promise 数组,当所有 Promise 都完成时,它返回一个新的 Promise,其结果是所有 Promise 的结果组成的数组。如果任何一个 Promise 失败,则返回的 Promise 也会失败。
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // Output: [1, 2, 3]
  })
  .catch(error => {
    console.error('Error:', error);
  });
  • Promise.race: 接受一个 Promise 数组,返回一个新的 Promise,该 Promise 会在第一个 Promise 完成或失败时完成。
const p1 = new Promise<number>((resolve) => setTimeout(() => resolve(1), 500));
const p2 = new Promise<number>((resolve) => setTimeout(() => resolve(2), 100));

Promise.race([p1, p2])
  .then(result => {
    console.log(result); // Output: 2 (因为 p2 先完成)
  });

7. 其他高级用法

Promise.allSettled

Promise.allSettled 接受一个 Promise 数组,返回一个新的 Promise,其结果是每个 Promise 的状态和结果组成的数组。

const p1 = Promise.resolve(1);
const p2 = Promise.reject(new Error('Failed'));
const p3 = Promise.resolve(3);

Promise.allSettled([p1, p2, p3])
  .then(results => {
    console.log(results);
    /*
    Output:
    [
      { status: 'fulfilled', value: 1 },
      { status: 'rejected', reason: Error: Failed },
      { status: 'fulfilled', value: 3 }
    ]
    */
  });

三、实际应用场景

1. 在请求接口中使用Promise

axios 是一个流行的 HTTP 客户端库,它基于 Promise,使得处理异步 HTTP 请求变得简单。你可以利用 axios 发起异步请求,并结合 Promise 的特性来处理请求的结果。以下是一些示例,展示如何在 axios 中使用 Promise 进行异步操作。

const axios = require('axios');

axios.post('https://api.example.com/data', { key: 'value' })
  .then(response => {
    console.log('Data:', response.data); // 处理响应数据
  })
  .catch(error => {
    console.error('Error:', error); // 处理错误
  });

2. 在需要等待执行的函数中使用Promsie


import JSZip from 'jszip'
import shp from 'shpjs'

const parseZip = async(zip)=>{
  const jsZip = new JSZip() // 解析zip数据为二进制数据
  const zipData = await jsZip.loadAsync(zip)
  const data = await zipData.generateAsync({ type: 'arraybuffer' }) // 将zip文件转化为二进制流
  return await shp(data) // 将二进制流转换为geojson数据
}

3. 循环中包含异步操作处理

在JavaScript中,循环的执行是同步的,这意味着一旦进入循环,它会逐次执行每个迭代,直到完成。因此,在循环完成之前,后续代码是不会被执行的。然而,如果你的循环中包含了异步操作(如网络请求、文件读写等),你可能需要使用异步机制来确保循环中的所有异步操作完成后再执行后续代码。

使用同步循环(简单示例)

如果你的循环是同步的(即没有异步操作),你不需要额外处理,只需确保循环完成后,代码会继续执行:

// 示例:同步循环
for (let i = 0; i < 10000; i++) {
  // 进行一些同步操作
  console.log(i);
}

// 循环结束后执行的代码
console.log("Loop finished.");
使用异步操作(例如异步请求)

如果你的循环中包含了异步操作,你可以使用 Promises 和 async/await 来确保在所有异步操作完成后再执行后续代码。以下是一个示例,展示了如何处理包含异步操作的循环:

// 模拟异步操作的函数
function asyncOperation(index) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(`Operation ${index} completed.`);
      resolve();
    }, Math.random() * 1000); // 随机延迟
  });
}

// 使用 async/await 确保循环中的所有异步操作完成
async function runAsyncLoop() {
  const promises = [];
  
  for (let i = 0; i < 10000; i++) {
    promises.push(asyncOperation(i)); // 将每个异步操作的 Promise 添加到数组中
  }

  await Promise.all(promises); // 等待所有异步操作完成

  // 循环完成后执行的代码
  console.log("All operations finished.");
}

runAsyncLoop();

解释:

  1. asyncOperation 是一个模拟异步操作的函数,返回一个 Promise
  2. runAsyncLoop 函数中,我们将每个异步操作的 Promise 存储在 promises 数组中。
  3. Promise.all(promises) 等待所有的 Promise 完成。
  4. 在所有异步操作完成后,执行 console.log("All operations finished.");

4. Promise.all实际应用场景

有一个人员列表,每个人员都有一个定位信息,需要通过调用高德导航接口查询出每个人员到目的地的距离和时间,然后根据距离和时间对人员列表排序。

代码实现过程:

// 传入人员列表数据 
async handleData(data) {
  return new Promise(async (resolve,reject)=>{
    let hasLngLatItems = [];
    let allRequest = [];
    try{
      data.forEach((item,index) => {
        // 只请求有位置的数据
        if(item.isonline == 1){
            hasLngLatItems.push(index)
            let url = "https://restapi.amap.com/v3/direction/driving?key=" + appConfig.gaodeApi + "&origin=" + this.fireLngLat[0] + "," + this.fireLngLat[1] + "&destination=" + item.lng + "," + item.lat + "&extensions=base";
            let request = axios({method: 'get',url: url})
            allRequest.push(request);
        }
      })
      // 所有请求完成 再执行排序
      let  responses = await  Promise.all(allRequest);

      // 将信息补充道数组中
      responses.forEach((res,idx)=>{
          if (res.data.route && res.data.route.paths && res.data.route.paths[0]) {
              let obj = res.data.route.paths[0]
              data[hasLngLatItems[idx]].distance = (obj.distance / 1000).toFixed(2)
              data[hasLngLatItems[idx]].duration = Math.round(obj.duration / 60)
          }
      })
      resolve(data);
    }catch(e){
      console.log(e);
      reject(null)
    }
  })
}

// 调用查询距离接口,对返回的结果进行排序
this.handleData(rel).then(dl => {

  if(dl){
    // _this.listData = dl
    // _this.allData = dl
    dl.forEach((item,index)=>{
      console.log("typeof",typeof dl[index].distance)
      if(typeof dl[index].distance != "undefined"){
        dl[index].distance = Number(dl[index].distance);
      }else{
        dl[index].distance = null;
      }
    })

    // 进行综合排序
    dl.sort((a,b) =>
      {
        
        if (a.distance === null && b.distance === null) return 0; // 两者都是 null,保持原有顺序
        if (a.distance === null) return 1; // a 是 null,排到后面
        if (b.distance === null) return -1; // b 是 null,排到后面

        if (a.isonline !== b.isonline) {
          return (a.isonline === b.isonline) ? 0 : a.isonline ? -1 : 1; // 在线用户优先
        }

        return a.distance - b.distance; // 对其他元素进行升序排序
      }
    )

    // 获取到全部的排序数据
    _this.listData = dl;
    _this.allData = dl;

    // 根据距离筛选
    setTimeout(() => {
      _this.radioChange()
    }, 1000)
  }
})

三、 总结

TypeScript 中的 Promise 提供了一种强大的方式来处理异步操作。通过利用 TypeScript 的类型系统和 async/await 语法,可以使异步代码更易于理解和维护。掌握 Promise 的各种方法和用法将帮助你编写更健壮和可维护的异步代码。如果你有更多问题或需要深入探讨,请告诉我!


http://www.kler.cn/a/391756.html

相关文章:

  • ArkTs简单入门案例:简单的图片切换应用界面
  • 时序数据库TimescaleDB安装部署以及常见使用
  • const限定符-C语言中指针的“可变与不可变”法则
  • 设计模式-七个基本原则之一-迪米特法则 + 案例
  • 计算机网络易混淆知识点串记
  • 智享AI 无人自动直播的崛起 ,引领智能互动与自动带货新潮流!
  • 8 ARM-PEG-FA由八个臂状结构的聚乙二醇(PEG)核心与叶酸(FA)分子通过化学连接而成
  • 什么是大数据治理?在企业数字化转型过程中有什么用?
  • PostgreSQL存储过程-pgAdmin
  • 命令行工具进阶指南
  • 【 AI写作鹅-注册安全分析报告-无验证方式导致安全隐患】
  • Flutter下拉刷新上拉加载的简单实现方式二
  • Lucene 和 Elasticsearch 中更好的二进制量化 (BBQ)
  • 【网络安全 | 甲方建设】DDOS 防范教程
  • 用AI绘画一周赚1W?怎么用AI绘画赚钱!
  • 数据驱动的投资分析:民锋科技的量化模型探索
  • WPF中MVVM工具包 CommunityToolkit.Mvvm
  • IOT物联网低代码可视化大屏解决方案汇总
  • 推荐一个Star超过2K的.Net轻量级的CMS开源项目
  • 语义通信论文略读(十二)图像知识库+styleGAN
  • C# 软件测试
  • vue写后台管理系统,有个需求将所有的$message消息提示换成确认框来增强消息提示效果,遇到嵌套过多的情况,出现某些问题
  • Seata源码笔记(三)
  • Spring Boot编程训练系统:架构设计与实现技巧
  • Vue自定义指令详解——以若依框架中封装指令为例分析
  • 从建立TRUST到实现FAIR:可持续海洋经济的数据管理