复习之深入Promise,实现ajax函数的封装

    技术2026-08-17  18

    什么是Promise

    JavaScript 这门语言是单线程执行的,这就导致一些耗时的操作会阻塞当前运行线程。为了解决这个问题,引入了“同步”和“异步”这两个概念。当多个异步任务需要顺序执行的时候,回调函数层层嵌套便产生了回调地狱。Promise,作为异步编程,回调地域的一种解决方案出现了。

    Promise对象具有以下两个特点:

    对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态: pending: 初始状态,不是成功或失败状态。fulfilled: 意味着操作成功完成。rejected: 意味着操作失败。

    只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。

    一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

    Promise的优点:可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,以此解决回调地狱问题。并且Promise 对象提供统一的接口,使得控制异步操作更加容易。

    Promise的缺点:一旦新建就会立刻执行,并且无法取消。并且需要设置回调函数才能在外部获取抛出的错误。处于初始状态panding 时不知道时刚刚开始还是即将完成。

    Promise的创建及使用

    1.Promise的创建及使用
    const fs = require('fs'); let promise = new Promise((resolve, reject) => { fs.readFile('./100.txt', 'utf8', (err, result) => { if (err != null) { reject(err); }else { resolve(result); } }); }); promise.then((result) => { console.log(result); }) .catch((err)=> { console.log(err); })
    2.链式调用,按顺序执行。解决回调地狱问题
    const fs = require('fs'); //解决回调地狱 // fs.readFile('./1.txt', 'utf8', (err, result1) => { // console.log(result1) // fs.readFile('./2.txt', 'utf8', (err, result2) => { // console.log(result2) // fs.readFile('./3.txt', 'utf8', (err, result3) => { // console.log(result3) // }) // }) // }); function p1 () { return new Promise ((resolve, reject) => { fs.readFile('./1.txt', 'utf8', (err, result) => { resolve(result) }) }); } function p2 () { return new Promise ((resolve, reject) => { fs.readFile('./2.txt', 'utf8', (err, result) => { resolve(result) }) }); } function p3 () { return new Promise ((resolve, reject) => { fs.readFile('./3.txt', 'utf8', (err, result) => { resolve(result) }) }); } p1().then((r1)=> { console.log(r1); return p2(); }) .then((r2)=> { console.log(r2); return p3(); }) .then((r3) => { console.log(r3) })
    3.用 Promise 对象实现的 Ajax 操作的例子。
    function ajax (options) { // 存储的是默认值 var defaults = { type: 'get', url: '', data: {}, header: { 'Content-Type': 'application/x-www-form-urlencoded' }, success: function () {}, error: function () {} }; // 使用options对象中的属性覆盖defaults对象中的属性 Object.assign(defaults, options); // 创建ajax对象 var xhr = new XMLHttpRequest(); // 拼接请求参数的变量 var params = ''; // 循环用户传递进来的对象格式参数 for (var attr in defaults.data) { // 将参数转换为字符串格式 params += attr + '=' + defaults.data[attr] + '&'; } // 将参数最后面的&截取掉 // 将截取的结果重新赋值给params变量 params = params.substr(0, params.length - 1); // 判断请求方式 if (defaults.type == 'get') { defaults.url = defaults.url + '?' + params; } /* { name: 'zhangsan', age: 20 } name=zhangsan&age=20 */ // 配置ajax对象 xhr.open(defaults.type, defaults.url); // 如果请求方式为post if (defaults.type == 'post') { // 用户希望的向服务器端传递的请求参数的类型 var contentType = defaults.header['Content-Type'] // 设置请求参数格式的类型 xhr.setRequestHeader('Content-Type', contentType); // 判断用户希望的请求参数格式的类型 // 如果类型为json if (contentType == 'application/json') { // 向服务器端传递json数据格式的参数 xhr.send(JSON.stringify(defaults.data)) }else { // 向服务器端传递普通类型的请求参数 xhr.send(params); } }else { // 发送请求 xhr.send(); } // 监听xhr对象下面的onload事件 // 当xhr对象接收完响应数据后触发 xhr.onload = function () { // xhr.getResponseHeader() // 获取响应头中的数据 var contentType = xhr.getResponseHeader('Content-Type'); // 服务器端返回的数据 var responseText = xhr.responseText; // 如果响应类型中包含applicaition/json if (contentType.includes('application/json')) { // 将json字符串转换为json对象 responseText = JSON.parse(responseText) } // 当http状态码等于200的时候 if (xhr.status == 200) { // 请求成功 调用处理成功情况的函数 defaults.success(responseText, xhr); }else { // 请求失败 调用处理失败情况的函数 defaults.error(responseText, xhr); } } } ajax({ type: 'post', // 请求地址 url: 'http://localhost:3000/responseData', success: function (data) { console.log('这里是success函数'); console.log(data) } }) /* 请求参数要考虑的问题 1.请求参数位置的问题 将请求参数传递到ajax函数内部, 在函数内部根据请求方式的不同将请求参数放置在不同的位置 get 放在请求地址的后面 post 放在send方法中 2.请求参数格式的问题 application/x-www-form-urlencoded 参数名称=参数值&参数名称=参数值 name=zhangsan&age=20 application/json {name: 'zhangsan', age: 20} 1.传递对象数据类型对于函数的调用者更加友好 2.在函数内部对象数据类型转换为字符串数据类型更加方便 */
    4.Promise.all方法,Promise.race方法
    Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例. var p = Promise.all([p1,p2,p3]);

    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。 var p = Promise.race([p1,p2,p3]);

    只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。

    Processed: 0.008, SQL: 9