Promise简单介绍

一、什么是Promise? 

         简单的说,Promie是ES6引入的,进行异步编程的一种新的解决方案(在ES5及之前,处理异步操作通常使用回调函数)。promise对象用来封装一个异步操作并可以获取其成功或失败的结果值。


二、为什么要使用Promise?

        支持链式调用,可以解决JavaScript中回调地狱(Callback Hell)问题;
        指定回调函数的方式更加灵活,使异步代码更易于编写和管理。


三、什么是回调地狱(Callback Hell)?

         是指在异步编程中,多个回调函数嵌套在一起形成的深层次、难以维护的代码结构。这种情况通常出现在使用回调函数处理异步操作的代码中,特别是在ES5及之前的JavaScript版本中。

asyncFunction1(opt, (...data1) => {
  asyncFunction2(opt, (...data2) => {
      asyncFunction3(opt, (...data3) => {
          asyncFunction4(opt, (...data4) => {
                  //... more nested callbacks ...
            });
        });
    });
});

        在上述代码中,每个异步操作的结果都依赖于前一个操作的结果,导致嵌套的回调函数越来越多,代码变得难以维护和理解。
        由此可以看出回调地狱的问题:

  • 可读性差:由于多个层级的回调函数,代码变得晦涩难懂,可读性大大降低。
  • 错误处理困难:在回调地狱中,错误处理需要在每个回调函数中进行,导致错误处理代码重复和冗余。 
  • 增加维护成本:当代码逻辑需要修改时,需要深入多层回调来理解和修改,增加了维护成本。 

四、Promise对象三种状态: 

  • Pending(进行中):初始状态,表示异步操作正在进行中。
  • Fulfilled(已成功):异步操作成功完成,并返回了结果,此时Promise会进入已成功状态。
  • Rejected(已失败):异步操作失败,出现错误,并返回错误信息,此时Promise会进入已失败状态。

        一个Promise对象一旦进入Fulfilled(已成功)或Rejected(已失败)状态,就称为“settled”状态,这个状态不可改变。      


五、创建Promise的一般语法:

const myPromise = new Promise((resolve, reject) => {
  // 异步操作代码
  // 如果操作成功,调用 resolve(value)
  // 如果操作失败,调用 reject(error)
});

六、Promise对象的主要方法:

        Promise对象的主要方法是`.then()`和`.catch()`,通过这两个方法可以处理异步操作的结果和错误:

  • .then(onFulfilled) :当Promise进入Fulfilled状态时,会调用`onFulfilled`函数来处理异步操作的成功结果。
  • .catch(onRejected):当Promise进入Rejected状态时,会调用`onRejected`函数来处理异步操作的失败情况。
function fetchUserData() {
  return new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
      const success = true; // 模拟成功情况,可以根据实际情况设置
      if (success) {
        const data = { name: "John", age: 30 };
        resolve(data); // 成功时调用resolve,并传递结果
      } else {
        reject("Error: Unable to fetch user data"); // 失败时调用reject,并传递错误信息
      }
    }, 2000); // 模拟异步延迟2秒
  });
}

// 使用Promise
fetchUserData()
  .then((data) => {
    console.log("User data:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

效果展示:
成功: 

失败: 

Promise简单介绍_第1张图片


七、回调函数和Promise对比示例:

        示例1,使用回调函数处理异步操作:

// 假设有一个异步函数,用于模拟从服务器获取数据
function fetchDataFromServer(callback) {
  setTimeout(() => {
    const data = { message: "Data received from server" };
    // 模拟成功获取数据
    callback(null, data); // 第一个参数为错误对象,null表示没有错误
    // 模拟出现错误的情况
    // callback("Error: Unable to fetch data");
  }, 2000); // 模拟异步延迟2秒
}

// 使用回调函数获取数据
fetchDataFromServer((error, data) => {
  if (error) {
    console.error("Error:", error);
  } else {
    console.log("Data:", data);
  }
});

效果展示:
成功:  

失败:  

Promise简单介绍_第2张图片

        在上述示例1中,fetchDataFromServer函数接受一个回调函数作为参数,并在异步操作完成后调用该回调函数。回调函数的第一个参数通常用于传递错误信息,第二个参数用于传递成功的结果。

        示例2,使用Promise处理异步操作:

// 假设有一个异步函数,用于模拟从服务器获取数据
function fetchDataFromServer() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true; // 模拟成功情况,可以根据实际情况设置
      if (success) {
        const data = { message: "成功接收到数据" };
        resolve(data); // 成功时调用resolve,并传递结果
      } else {
        reject("接收数据失败!"); // 失败时调用reject,并传递错误信息
      }
    }, 2000); // 模拟异步延迟2秒
  });
}

// 使用Promise获取数据
fetchDataFromServer()
  .then((data) => {
    console.log("Data:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

效果展示:
成功:   

 失败:  

Promise简单介绍_第3张图片

        在上述示例2中,fetchDataFromServer函数返回一个Promise对象。通过.then()方法处理成功的情况,通过.catch()方法处理失败的情况。

        示例1和示例2对比分析: 

  • 使用回调函数时,代码会出现多层嵌套,可能导致回调地狱,特别是当处理多个异步操作时。
  • 使用Promise时,通过链式调用.then().catch(),代码结构更加清晰和易于阅读。

 八、举例其他异步函数操作:

        举简单的例子,在fs文件操作、数据库操作、AJAX操作以及定时器中,如何使用回调函数和Promise函数,进行对照学习。

        8.1、文件操作(fs): 

         使用回调函数:

//使用Node.js的fs模块进行文件读取
const fs = require('fs');

// 使用回调函数读取文件
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Error:', err);
  } else {
    console.log('Data from file:', data);
  }
});

效果展示:
成功:

Promise简单介绍_第4张图片

 失败:  

Promise简单介绍_第5张图片

        使用Promise: 

const fs = require('fs/promises');

// 使用Promise读取文件
fs.readFile('example.txt', 'utf8')
  .then((data) => {
    console.log('Data from file:', data);
  })
  .catch((err) => {
    console.error('Error:', err);
  });

效果展示:
成功: 

Promise简单介绍_第6张图片  失败:  Promise简单介绍_第7张图片

        8.2、数据库操作: 

        首先,确保已经安装了mysql2库(或其他MySQL连接库),然后通过该库来连接数据库。 

npm install mysql2

        使用回调函数:

//此处使用Mysql数据库进行数据查询
const mysql = require('mysql2');

// 创建数据库连接池
const pool = mysql.createPool({
  host: 'your_database_host',
  user: 'your_database_user',
  password: 'your_database_password',
  database: 'your_database_name',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// 示例:执行查询操作
const query = 'SELECT * FROM account WHERE money > ?';
const values = [1000];

pool.getConnection((err, connection) => {
  if (err) {
    console.error('Error:', err);
    return;
  }

  connection.query(query, values, (error, results) => {
    connection.release();

    if (error) {
      console.error('Error:', error);
    } else {
      console.log('Query results:', results);
    }
  });
});

效果展示:
成功:

 

失败:  

Promise简单介绍_第8张图片

        使用Promise:  

const mysql = require('mysql2');

// 创建数据库连接池
const pool = mysql.createPool({
  host: 'your_database_host',
  user: 'your_database_user',
  password: 'your_database_password',
  database: 'your_database_name',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// 封装查询操作为Promise函数
function executeQuery(query, values) {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        reject(err);
        return;
      }

      connection.query(query, values, (error, results) => {
        connection.release();

        if (error) {
          reject(error);
        } else {
          resolve(results);
        }
      });
    });
  });
}

// 示例:执行查询操作
const query = 'SELECT * FROM account WHERE monrey > ?';
const values = [1000];

executeQuery(query, values)
  .then((results) => {
    console.log('Query results:', results);
  })
  .catch((error) => {
    console.error('Error:', error);
  });

效果展示:
成功:

失败:  

Promise简单介绍_第9张图片

        8.3、AJAX操作: 

         使用回调函数:

function sendAJAX(url, successCallback, errorCallback) {
  const xhr = new XMLHttpRequest();
  xhr.responseType = 'json';
  xhr.open('GET', url);
  xhr.send();

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        successCallback(xhr.response);
      } else {
        errorCallback(xhr.status);
      }
    }
  };
}

// Example usage with callbacks
sendAJAX(
  'https://api.apiopen.top/getJoke',
  function (data) {
    console.log('Data:', '请求成功');
  },
  function (error) {
    console.error('Error:', '请求失败');
  }
);

效果展示:
成功:

失败:   

Promise简单介绍_第10张图片

 

        使用Promise:  

function sendAJAX(url){
	return new Promise((resolve,reject)=>{
		const xhr = new XMLHttpRequest();
		xhr.responseType = 'json';
		xhr.open("GET",url);
		xhr.send();
		//处理结果
		xhr.onreadystatechange = function(){
			if(xhr.readyState === 4 ){
				//判断成功
				if(xhr.status >= 200 && xhr.status <300){
					resolve(xhr.response);
				}else{
					reject(xhr.status);
				}
			}
		}
	})
}
// Example usage with Promise
sendAJAX('https://api.apiopen.top/getJoke')
	.then((data) => {
		console.log('Data:',"请求成功");
	})
	.catch((error) => {
		console.error('Error:', "请求失败");
	});

效果展示:
成功:

失败:   

Promise简单介绍_第11张图片

 

        8.4、定时器:

         使用回调函数:

//此处使用定时器函数setTimeout和setInterval进行操作
// 使用回调函数实现定时器
function doSomething(callback) {
  setTimeout(() => {
    console.log('Doing something...');
    callback();
  }, 2000); // 2秒后执行回调
}

// 使用回调函数执行定时器操作
doSomething(() => {
  console.log('Callback executed after 2 seconds.');
});

        使用Promise:  

// 使用Promise实现定时器
function doSomething() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Doing something...');
      resolve();
    }, 2000); // 2秒后resolve
  });
}

// 使用Promise执行定时器操作
doSomething()
  .then(() => {
    console.log('Promise resolved after 2 seconds.');
  });

 效果展示:


九:Promise函数基本流程和相关API: 

        综上学习,可以看到Promise函数的基本流程如图所示:

Promise简单介绍_第12张图片         除了上述看到的Promise.resolved()、Promise.rejected()、Promise.then()、Promise.catch(),其他常用的API还有:Promise.all(iterable)、Promise.race(iterable)等等。

        Promise.all(iterable):接收一个可迭代对象iterable(如数组或类数组对象),返回一个新的Promise对象。该Promise对象在iterable中的所有Promise对象都解决(Fulfilled)时才会解决,并将所有解决结果以数组形式返回;如果iterable中有任何一个Promise对象拒绝(Rejected),则该Promise对象立即拒绝,并返回第一个拒绝的原因。 

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values); // Output: [1, 2, 3]
  })
  .catch((error) => {
    console.error(error);
  });

效果展示: 

        Promise.race(iterable):类似于Promise.all(),但是只要iterable中的任何一个Promise对象解决或拒绝,返回的Promise对象就会立即解决或拒绝,并采用第一个解决或拒绝的结果。 

const promise1 = new Promise((resolve) => setTimeout(resolve, 1000, "one"));
const promise2 = new Promise((resolve) => setTimeout(resolve, 2000, "two"));

Promise.race([promise1, promise2])
  .then((value) => {
    console.log(value); // Output: "one" (promise1 resolves faster)
  })
  .catch((error) => {
    console.error(error);
  });

效果展示: 


十:async函数和await表达式

        10.1 async函数 

         函数的返回值为promise对象,且promise对象的结果是由async函数的返回值决定。它其实是用来定义一个返回 Promise 对象的异步函数。

async function main(){

}
let result = main();
console.info(result);

        此时看控制台可以看到,返回值是一个promise对象,且状态是"fulfilled"(已成功)。

         1.如果返回的值是一个非Promise类型的数据:

  
    
  

效果展示: 

        2.返回的是一个Promise对象: 

 
    
  

成功: 

失败: 

Promise简单介绍_第13张图片

        3.直接抛出一个异常: 

  
    
  

效果展示: 

Promise简单介绍_第14张图片

        10.2 await 表达式

        await右侧的表达式一般为promise对象,返回的是promise成功的值。若是表达式时其他值,则直接将此值作为await的返回值。
        注意:await必须写在async函数中,但是async函数中可以没有await。若是await中的promise失败了,就会抛出异常,需要通过try...catch捕获处理。

         1、直接使用await:

  
    
  

效果展示: 
         2.右侧为promise对象:

  
    
  

效果展示:  

        3.右侧为其他: 

  
    
  

效果展示: 

        4. promise是失败的状态:

  
    
  

 效果展示: 

        10.3 async和await结合使用

         使用回调函数实现实例:

//使用Node.js的fs模块进行文件读取
const fs = require('fs');

// 使用回调函数读取文件
fs.readFile('./example.txt', 'utf8', (err, data1) => {
  if(err) throw err;
  fs.readFile('./example2.txt', 'utf8', (err, data2) => {
    if(err) throw err;
    fs.readFile('./example3.txt', 'utf8', (err, data3) => {
      if(err) throw err;
      console.log(data1 + data2 + data3)
    });
  });
});

        使用async函数+await实现示例: 

const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
async function main(){
  try {
    let data1 = await mineReadFile('./example.txt');
    let data2 = await mineReadFile('./example2.txt');
    let data3 = await mineReadFile('./exampl23.txt');
    console.log(data1 + data2 + data3)
  } catch (error) {
    console.log(error)
  }
}
main();

效果展示:
成功:

Promise简单介绍_第15张图片

失败: 

Promise简单介绍_第16张图片


十一:总结

        Promise对象是用于处理异步操作的特殊对象,通过其构造函数创建,并传递一个executor函数,内部包含异步操作,当异步操作完成时,通过resolve方法将Promise对象从Pending状态转换为Fulfilled状态,将结果传递给处理函数;若异步操作失败,则通过reject方法将Promise对象转换为Rejected状态,传递错误信息。我们可以使用.then()和.catch()来处理成功和失败的情况,使得异步编程更加清晰、简洁,避免回调地狱,提供了更强大的异步编程能力。 

你可能感兴趣的:(Web端学习,学习,前端,javascript)