Node.js异步编程——Promise对象实现

一、Promise对象简介

Promise是一个在ES6语法中引进的专门用于处理异步操作的对。

Promise代表的含义是异步操作的结果,即一个在现在、未来或永远不可用的值,通过Promise可以判断异步操作的顺利完成或失败。

在异步编程场景中,一般将Promise对象作为函数的返回结果,我们可以将Promise对象与回调函数进行绑定,将回调函数传给Promise对象。比起在指定的函数中调用回调函数,Promise对象提供了更加灵活的异步编程方法。

Promise对象在代码运行期间主要呈现三种状态:

Pending:任务处于初始状态。

Fulfilled:任务执行成功,结果可用。

Rejected:任务执行失败,并显示错误原因。

Promise对象的大致语法如下:

let promise_obj = new Promise((resolve, reject) => {
    // 执行异步操作
    if (isSuccessful) {
        resolve("operation successful");
        // 切换到fulfilled状态
    } else {
        reject("operation failed");
        // 切换到rejected状态
    }
});

代码样例:判断一个数是否为偶数

let checkEven = new Promise((resolve, reject) => {
    let number = 6;
    if (number % 2 === 0) {
        resolve("The number is even!");
    }
    else {
        reject("The number is odd!");
    }
});
checkEven
    .then((message) => console.log(message)) // On success
    .catch((error) => console.error(error)); // On failure

运行结果:

The number is even!

二、创建Promise对象的方法

创建Promise对象时,需要将executor函数传递给Promise构造函数,该构造函数包含异步操作,并定义了异步操作执行成功和执行失败时对应的动作。

resolve和reject是构造Promise对象时默认要传入的两个参数。

Promise构造函数的语法:

let promise = new Promise(executor_function(resolve, reject) {
  // async operation code
});

Promise构造函数的匿名函数写法:

let promise = new Promise((resolve, reject) => {
  // async operation code
});

三、Promise对象的状态切换

Promise对象的状态切换遵循严格的生命周期规则,pending是初始状态,表示异步操作尚未完成。当异步操作执行成功时,状态变为fulfilled,此时会调用resolve函数,并且Promise.then()方法中的成功回调会被触发。当异步操作执行失败时,状态变为rejected,此时会调用reject函数,Promise.then()或Promise.catch()中的错误处理回调会被触发。

1.初始状态

构造Promise对象时的状态,此时状态为pending,既不成功也不失败。

对应Node.Js代码:

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

2.执行成功

状态从pending切换到fulfilled,通过resolve触发。

对应Node.Js代码:

const successPromise = new Promise((resolve) => {
  setTimeout(() => resolve('success'), 1000); 
  //1秒后状态变为fulfilled
});

3.执行失败

状态从pending切换到rejected,通过reject或异常抛出来触发。

对应Node.Js代码:

const failPromise = new Promise((_, reject) => {
  setTimeout(() => reject(new Error("Failed")), 1000); 
  //1秒后状态变为rejected
});

const errorPromise = new Promise(() => {
  throw new Error("Exception occured."); 
  //状态立即变为rejected
});

4.状态切换对应的函数触发

successPromise.then(value => {
  console.log(value);   
  //在fulfilled状态触发
});

failPromise.catch(error => {
  console.error(error); 
  //在rejected状态触发
});

// ES2018标准引入
errorPromise.finally(() => {
  console.log('状态已确定'); 
  //无论成功失败都会触发
});

5.状态转换规则

每个Promise对象只能进行一次状态切换。

切换后的状态将会被永久保持(settled)。

发生的每一个rejected都要有对应的处理,未被处理的rejected状态会导致UnhandledPromiseRejectionWarning异常。

只有等异步操作完成后才会开始触发状态的变更。

四、Promise对象的常用函数接口

1.Promise.then()函数

用于链式处理异步操作的结果,处理Promise的fullfiled或reject状态。

函数语法:

Promise.then(onFulfilled, onRejected)

代码样例:

const promise = new Promise((resolve) => resolve("success !"));
promise.then(
  (value) => console.log(value),
  (error) => console.error(error)
);

运行结果:

success !

2.Promise.catch()函数

专门用于处理Promise的reject状态。

函数语法:

Promise.catch(onRejected)

代码样例:

const promise1 = new Promise((resolve, reject) => {
    reject("Failed !");
  });
promise1.catch((error) => {
    console.error(error);
  });

运行结果:

Failed !

3.Promise.finally()函数

经常在清理资源时使用。

无论Promise成功或失败都会执行的函数。

函数语法:

Promise.finally(onFinally)

代码样例:

const p1 = Promise.resolve("Success.");
p1.then((result) => console.log(result))
    .catch((error) => console.error(error))
    .finally(() => console.log("Cleanup completed"));

运行结果:

Success.
Cleanup completed

4.Promise.all()函数

需要并行运行多个Promise并等待所有Promise执行完成时使用。

将可迭代的Promise数组作为输入,并返回单个Promise对象。

当所有Promise都执行成功时,返回fullfiled状态的Promise,如果有任何一个Promise执行失败,则返回reject状态的Promise。

函数语法:

Promise.all(iterable)

代码样例:

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' ]

5.Promise.allSettled()函数

适合需要知道所有异步操作最终状态的场景,它可以等待所有Promise都完成,无论成功或失败。

将可迭代的Promise数组作为输入,并返回单个Promise对象。

当所有Promise都settle时,返回Promise结果。

函数语法:

Promise.allSettled(iterable)

代码样例:

const p1 = Promise.resolve(1);
const p2 = Promise.reject("Failed.");
Promise.allSettled([p1, p2]).then((results) => {
  console.log(results);
});

运行结果:

[
  { status: 'fulfilled', value: 1 },
  { status: 'rejected', reason: 'Failed.' }
]

6.Promise.race()函数

常用于请求的超时处理。

将可迭代的Promise数组作为输入,并返回单个Promise对象。

返回的Promise结果与第一个settle的Promise结果一致。

函数语法:

Promise.race(iterable)

代码样例:

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

五、Promise的异步编程实践

Demo1:链式调用

function asyncTask1() {
    return new Promise(resolve => {
      setTimeout(() => resolve(100), 1000);
    });
  }
asyncTask1()
    .then(result => {
      console.log(result);
      return result * 2;
    })
    .then(newResult => {
      console.log(newResult);
    });

运行结果:

100
200

Demo2:配合fetch()使用

fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => response.json())
    .then(data => console.log(data));
fetch('https://jsonplaceholder.typicode.com/todos/2')
    .then(response => response.json())
    .then(data => console.log(data));

运行结果:

{
  userId: 1,
  id: 2,
  title: 'quis ut nam facilis et officia qui',
  completed: false
}
{ userId: 1, id: 1, title: 'delectus aut autem', completed: false }

Demo3:Web网页的动态交互

<script>
  function askForName() {
    const name = window.prompt('What is your name?');
    
    const elem = document.querySelector('#greeting');
    elem.innerText = `Hi ${name}!`
  }
</script>

<button onclick="askForName()">
  Click me
</button>
<div id="greeting"></div>

运行结果:

Demo4:模拟异步的数据加载

<!DOCTYPE html>
<html>
<head>
    <title>Promise交互示例</title>
    <style>
        .container {
            padding: 20px;
            text-align: center;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
        }
        .loader {
            display: none;
            margin: 20px;
        }
        #result {
            margin-top: 20px;
            padding: 15px;
            border: 1px solid #ddd;
            min-height: 50px;
        }
    </style>
</head>
<body>
    <div class="container">
        <button id="loadBtn">加载数据</button>
        <div class="loader">加载中...</div>
        <div id="result"></div>
    </div>
    <script>
    document.getElementById('loadBtn').addEventListener('click', () => {
    const loader = document.querySelector('.loader');
    const resultDiv = document.getElementById('result');
   
    // 显示加载状态
    loader.style.display = 'block';
    resultDiv.innerHTML = '';
   
    // 创建Promise进行异步操作
    new Promise((resolve, reject) => {
        // 模拟API请求
        setTimeout(() => {
            const success = Math.random() > 0.2; // 80%成功率
            success ? resolve({ data: '成功获取的数据!' }) : reject('服务器错误');
        }, 1500);
    })
    .then(response => {
        resultDiv.innerHTML = response.data;
        resultDiv.style.color = 'green';
    })
    .catch(error => {
        resultDiv.innerHTML = `错误:${error}`;
        resultDiv.style.color = 'red';
    })
    .finally(() => {
        loader.style.display = 'none';
    });
});
    </script>
</body>
</html>

运行结果:

参考阅读:

https://javascript.info/Promise-basics

https://www.geeksforgeeks.org/javascript-Promise/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员与背包客_CoderZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值