Typescript的Promise原理及应用场景

Typescript的Promise原理及应用场景

Promise的基本原理

TypeScript 是一种静态类型检查的编程语言,它是 JavaScript 的超集。它允许开发者在编写代码时定义变量、函数和对象的类型,并在编译时进行类型检查。这使得我们能够在开发过程中发现潜在的错误,并提供更好的代码提示和自动补全功能。

现在,让我们来谈谈 Promise。Promise 是一种用于处理异步操作的对象,它代表了一个异步操作的最终完成(或失败)及其结果的值。Promise 提供了一种更优雅的方式来处理回调函数,并使我们能够更方便地进行异步编程。

要手写一个 Promise,我们需要了解它的基本原理。一个 Promise 主要有三个状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。在创建 Promise 对象时,我们需要提供一个执行器函数,该函数接受两个参数:resolve 和 reject。当异步操作成功完成时,我们调用 resolve,并传递结果值;当操作失败时,我们调用 reject,并传递失败的原因。

Promise的简单实现

好了,让我们尝试手写一个简单的 Promise 实现,并应用到一个真实场景中。我们将以获取用户信息的异步操作为例。

class MyPromise {
  private state: string;
  private value: any;
  private onFulfilledCallbacks: Array<Function>;
  private onRejectedCallbacks: Array<Function>;

  constructor(executor: Function) {
    this.state = 'pending';
    this.value = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value: any) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach(callback => callback(this.value));
      }
    };

    const reject = (reason: any) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.value = reason;
        this.onRejectedCallbacks.forEach(callback => callback(this.value));
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled: Function, onRejected: Function) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    } else if (this.state === 'rejected') {
      onRejected(this.value);
    } else {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  }
}

// 使用 Promise 获取用户信息的示例
const getUserInfo = new MyPromise((resolve, reject) => {
  // 模拟异步操作,比如发送请求获取用户信息
  setTimeout(() => {
    const user = { name: 'Alice', age: 25 };
    if (user) {
      resolve(user);
    } else {
      reject('Failed to get user information');
    }
  }, 2000);
});

// 处理获取用户信息成功的情况
getUserInfo.then(user => {
  console.log('User information:', user);
}).catch(error => {
  console.error('Error:', error);
});

在上面的示例中,我们创建了一个简单的 Promise 对象,并模拟了一个异步操作(通过 setTimeout)来获取用户信息。然后我们使用 then 方法来处理异步操作成功的情况,使用 catch 方法来处理异步操作失败的情况。

Promise的其他方法

除了 thencatch 方法之外,Promise 还提供了其他一些有用的方法,例如:

  • finally 方法:它接收一个回调函数,在 Promise 完成后无论成功或失败都会执行该回调。这在需要执行清理操作或无论结果如何都要执行一些操作时非常有用。

下面是一个使用 finally 方法的示例:

getUserInfo
  .then(user => {
    console.log('User information:', user);
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Cleanup operation here');
  });
  • Promise.all 方法:它接收一个 Promise 数组作为参数,并返回一个新的 Promise。该新 Promise 在所有传入的 Promise 都成功完成时才会成功,并返回一个包含所有 Promise 结果的数组;如果任何一个 Promise 失败,则新 Promise 会立即失败。

下面是一个使用 Promise.all 方法的示例:

const promise1 = new MyPromise(resolve => {
  setTimeout(() => resolve('Promise 1'), 1000);
});

const promise2 = new MyPromise(resolve => {
  setTimeout(() => resolve('Promise 2'), 2000);
});

const promise3 = new MyPromise(resolve => {
  setTimeout(() => resolve('Promise 3'), 1500);
});

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log('Results:', results);
  })
  .catch(error => {
    console.error('Error:', error);
  });

在上述示例中,我们创建了三个 Promise,分别模拟了不同的异步操作。通过 Promise.all 方法,我们将这三个 Promise 放入一个数组中,并在它们都成功完成后打印出结果。

异步编程的async/await语法

在使用 Promise 进行异步编程时,我们经常会遇到需要处理多个异步操作的情况。为了更方便地管理这些操作,ES6 引入了 async/await 语法,它基于 Promise,并提供了一种更简洁的方式来编写异步代码。

async/await 允许我们使用类似同步代码的方式编写异步代码,而不需要显式地调用 .then 方法。下面是一个使用 async/await 的示例:

function delay(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function fetchData() {
  try {
    console.log('Fetching data...');
    await delay(2000);
    const data = 'Some data';
    console.log('Data fetched:', data);
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

async function processData() {
  try {
    const result = await fetchData();
    console.log('Processing data:', result);
    // 执行其他操作...
  } catch (error) {
    console.error('Error:', error);
  }
}

processData();

在上述示例中,我们定义了一个 fetchData 函数,它使用 delay 函数模拟异步操作,并返回一些数据。我们使用 async 关键字标记 fetchData 函数为异步函数,这样我们就可以在函数体内使用 await 关键字来等待异步操作完成。

然后,我们定义了另一个 processData 函数,它使用 await 关键字调用 fetchData 函数,并在异步操作完成后处理数据。在 processData 函数内部,我们可以像处理同步代码一样处理异步操作的结果。

async/await 使异步代码的编写和理解更加直观和简单,特别是在处理复杂的异步逻辑时。你可以尝试在你的项目中应用 async/await,它会极大地提升你的开发效率。

Promise.all 的原理

首先,让我们看一下 Promise.all 的原理。Promise.all 接收一个 Promise 数组作为参数,并返回一个新的 Promise。新的 Promise 在所有传入的 Promise 都成功完成时才会成功,并返回一个包含所有 Promise 结果的数组;如果任何一个 Promise 失败,则新的 Promise 会立即失败。

下面是一个手写实现 Promise.all 的示例代码:

function promiseAll(promises: Promise<any>[]): Promise<any[]> {
  return new Promise((resolve, reject) => {
    const results: any[] = [];
    let completedCount = 0;

    if (promises.length === 0) {
      resolve(results);
    }

    promises.forEach((promise, index) => {
      promise
        .then(result => {
          results[index] = result;
          completedCount++;

          if (completedCount === promises.length) {
            resolve(results);
          }
        })
        .catch(reject);
    });
  });
}

在上述代码中,我们定义了一个 promiseAll 函数,它接收一个 Promise 数组作为参数,并返回一个新的 Promise。

promiseAll 函数中,我们定义了一个 results 数组来存储每个 Promise 的结果。completedCount 变量用于记录已完成的 Promise 数量。

我们首先检查传入的 Promise 数组的长度,如果是空数组,则立即将新的 Promise 解析为一个空数组,并调用 resolve

然后,我们遍历每个 Promise,并使用 .then 方法来处理成功的情况。当一个 Promise 成功完成时,我们将其结果存储在 results 数组的相应位置,并递增 completedCount

最后,我们在所有 Promise 都完成时(completedCount 等于 Promise 数组的长度),调用 resolve 并传递 results 数组。

现在,让我们看一个使用 Promise.all 的应用场景。假设我们有一个需要同时发送多个请求的情况,而且只有当所有请求都成功返回时,我们才希望继续处理数据。

function fetchData(url: string): Promise<any> {
  return new Promise((resolve, reject) => {
    // 发送请求并获取数据
    // 假设我们使用 fetch API 发送请求
    fetch(url)
      .then(response => response.json())
      .then(data => resolve(data))
      .catch(error => reject(error));
  });
}

const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  'https://api.example.com/data3'
];

promiseAll(urls.map(url => fetchData(url)))
  .then(results => {
    console.log('All data:', results);
    // 处理所有数据...
  })
  .catch(error => {
    console.error('Error:', error);
    // 处理错误...
  });

在上述示例中,我们有一个包含多个 URL 的数组 urls,我们希望同时发送多个请求并获取它们的数据。我们使用 urls.map 方法将每个 URL 映射为一个调用 fetchData 函数的 Promise。

然后,我们使用 promiseAll 函数将这些 Promise 组合起来,并在它们都成功完成时获取结果。在 .then 方法中,我们可以访问包含所有请求数据的 results 数组,并继续处理数据。

这是一个使用 Promise.all 的典型场景,它使得在需要同时处理多个异步操作结果的情况下,代码更简洁、可读性更好,并且能够提高性能。

Promise.race的原理

除了 Promise.all,Promise 还提供了其他一些有用的方法,让我们来了解一下其中的两个。

Promise.race 方法:它接收一个 Promise 数组作为参数,并返回一个新的 Promise。这个新 Promise 将在传入的 Promise 数组中的任何一个 Promise 成功或失败时立即解析,并采用第一个解析或拒绝的 Promise 的结果。

下面是一个使用 Promise.race 的示例:

const promise1 = new Promise(resolve => setTimeout(() => resolve('Promise 1'), 2000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('Promise 2'), 1000));

Promise.race([promise1, promise2])
  .then(result => {
    console.log('Result:', result);
  })
  .catch(error => {
    console.error('Error:', error);
  });

在上述示例中,我们创建了两个 Promise,并使用 Promise.race 方法将它们放入一个数组中。这个新的 Promise 在第一个 Promise 成功或失败时立即解析,并返回相应的结果。

另外,要注意使用 Promise 时需要处理错误。你可以使用 .catch 方法或在 .then 方法链的最后添加一个 .catch 来捕获并处理 Promise 的拒绝(失败)情况。

"Promise.race" 方法的底层原理很简单。它接收一个 Promise 数组作为参数,并返回一个新的 Promise。这个新 Promise 将在传入的 Promise 数组中的任何一个 Promise 成功或失败时立即解析,并采用第一个解析或拒绝的 Promise 的结果。

下面是一个手写实现 "Promise.race" 的示例代码:

function promiseRace(promises: Promise<any>[]): Promise<any> {
  return new Promise((resolve, reject) => {
    promises.forEach(promise => {
      promise
        .then(resolve)
        .catch(reject);
    });
  });
}

在上述代码中,我们定义了一个 "promiseRace" 函数,它接收一个 Promise 数组作为参数,并返回一个新的 Promise。

在 "promiseRace" 函数中,我们遍历传入的 Promise 数组,并对每个 Promise 使用 ".then" 方法来处理解析(成功)情况。当任何一个 Promise 成功解析时,我们调用 "resolve" 函数并传递相应的结果。

如果其中任何一个 Promise 失败(拒绝),我们使用 ".catch" 方法来捕获错误,并调用 "reject" 函数将错误传递给新的 Promise。

这样,当传入的 Promise 数组中的任何一个 Promise 成功或失败时,新的 Promise 就会立即解析并采用第一个解析或拒绝的 Promise 的结果。

现在让我们看一个使用 "Promise.race" 的示例场景。

假设我们需要发送多个请求并使用第一个返回的结果,而忽略其余请求。我们可以使用 "Promise.race" 来实现这个场景。

function fetchData(url: string): Promise<any> {
  return new Promise((resolve, reject) => {
    // 发送请求并获取数据
    // 假设我们使用 fetch API 发送请求
    fetch(url)
      .then(response => response.json())
      .then(data => resolve(data))
      .catch(error => reject(error));
  });
}

const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  'https://api.example.com/data3'
];

promiseRace(urls.map(url => fetchData(url)))
  .then(result => {
    console.log('First result:', result);
    // 处理第一个结果...
  })
  .catch(error => {
    console.error('Error:', error);
    // 处理错误...
  });

在上述示例中,我们有一个包含多个 URL 的数组 "urls"。我们希望同时发送多个请求,并使用第一个返回的结果进行处理。

我们使用 ".map" 方法将每个 URL 映射为一个调用 "fetchData" 函数的 Promise。

然后,我们使用 "promiseRace" 函数将这些 Promise 组合起来,并在第一个 Promise 返回结果时获取该结果。在 ".then" 方法中,我们可以访问第一个返回的结果,并进行相应的处理。

这是一个使用 "Promise.race" 的典型场景,它使得在需要获取并使用最先返回的结果时,我们可以更加灵活地处理异步操作

总结

这些是 Promise 的一些基本概念和常用方法。通过手写 Promise 的代码并应用到真实场景中,你可以更好地理解 Promise 的工作原理和灵活性。希望这些示例能够帮助你更深入地掌握 TypeScript 和 Promise。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TypeScript 装饰器的主要应用场景有以下几种: 1. 类装饰器: 用于修改类的行为,例如添加静态属性和方法、重写构造函数等。 2. 方法装饰器: 用于修改类中某个方法的行为,例如在方法调用前后执行特定代码、更改方法的返回值等。 3. 访问器装饰器: 用于修改类中属性访问器的行为,例如在获取或设置属性值时执行特定代码等。 4. 属性装饰器: 用于修改类中某个属性的行为,例如对属性进行只读设置、验证属性值是否符合要求等 5. 参数装饰器: 修改方法/构造函数中参数的属性,例如添加验证,或者标记参数是否可选等。 装饰器的应用可以使得我们的代码变得更加灵活,并且可以在不改变原有代码结构的情况下对其进行拓展和优化. ### 回答2: TypeScript的装饰器主要用于在类、方法、属性或参数上添加元数据或额外的逻辑。它是一种特殊类型的声明,可以附加到类声明、方法、属性或参数上,并包含有关这些声明的额外信息。 主要应用场景有以下几个方面: 1. 类装饰器:可以用于修改类的行为。比如可以使用类装饰器来对类的构造函数进行修改,例如添加新的属性或方法,或者对原有的属性和方法进行修饰。 2. 方法装饰器:可以用于修改方法的行为。比如可以使用方法装饰器来拦截方法的调用,修改方法参数或返回值,或者在方法执行前后添加额外的逻辑。 3. 属性装饰器:可以用于修改类的属性。比如可以使用属性装饰器来修改属性的属性描述符,添加属性值的验证和转换逻辑。 4. 参数装饰器:可以用于修改方法的参数行为。比如可以使用参数装饰器来对方法的参数进行验证、转换或添加元数据。 通过使用装饰器,我们可以在不修改原有代码的情况下,动态地扩展和修改类、方法、属性或参数的行为。装饰器提供了一种灵活和可复用的方式来实现对代码的注解和修改。它在许多框架和库中都得到了广泛的应用,例如Angular框架中的依赖注入和路由配置等功能就使用了装饰器。 ### 回答3: TypeScript装饰器是一种特殊类型的声明,可以附加到类、方法、属性或参数上,可以用于修改类的行为或元数据。主要的应用场景包括: 1. 修改类的行为: 装饰器可以用于修改类的行为,例如增加类的方法或属性,对类的属性进行校验或者限制等。通过使用装饰器,可以在不修改源代码的情况下给类添加额外的功能。 2. 日志记录: 装饰器可以用于记录类或方法的调用日志,包括方法的入参和出参。这对于调试和排查问题非常有帮助,可以快速定位问题所在。 3. 访问权限控制: 装饰器可以用于控制类的方法或属性的访问权限,例如限制只有特定用户或角色才能访问某个方法或属性。 4. 验证和校验: 装饰器可以用于验证和校验类的属性的合法性,例如对用户输入进行校验,确保输入的数据符合规定的格式和要求。 5. 依赖注入: 装饰器可以用于实现依赖注入,即将某个对象注入到类的构造函数中,使得类可以使用这个对象的功能。 总之,TypeScript装饰器提供了一种灵活且强大的方式来扩展和修改类的行为,使得代码更加可重用、可扩展和易于维护。它可以应用于各种场景,提高开发效率和代码质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值