ToB企服应用市场:ToB评测及商务社交产业平台

标题: 前端Promise [打印本页]

作者: 饭宝    时间: 2024-6-22 12:51
标题: 前端Promise
目录

一、Promise的明白和使用
1、Promise是什么?
2、为什么要用Promise?
二、Promise使用案例
1、抽奖案例
2、fs读文件案例
3、AJAX请求
4、函数封装promise读文件fs
5、util.promise方法举行promise风格转化
6、封装AJAX请求
三、Promise对象状态属性
四、promise对象效果值属性
五、Promise的API
1、Promise构造函数:Promise (executor) { }
2、Promise.prototype.then方法
3、Promise.prototype.catch方法
4、Promise.resolve 方法 
5、Promise.reject方法
6、Promise.all方法
7、Promise.race方法
六、Promise的几个关键问题
1、怎样改变Promise的状态?
2、一个Promise指定多个成功/失败回调函数,都会调用!
3、改变Promise状态和指定回调函数谁先谁后?
4、promise.then()返回的新promise的效果状态由什么决定?
5、promise怎样串联多个操作使命
6、promise非常穿透?
7、promise链怎么中断?
七、手写Promise
八、async函数
九、await表达式

一、Promise的明白和使用
1、Promise是什么?

抽象定义:
1).es6中一门新的技术
2).是JS中举行异步编程的新的办理方案,旧的方案是回调异步使命
具体寄义:
1).promise是一个构造函数
2).promise对象可以用来封装一个异步操作并且可以获取其成功/失败的效果
异步编程有:fs文件操作、数据库操作、定时器、AJAX等
2、为什么要用Promise?

二、Promise使用案例

1、抽奖案例

        对两种不同的执行环境用两个函数返回,函数传参即为必要的数据,然后使用实例对象.then( ()=>{}, ()=>{} )往返调两个函数,函数的顺序就是在构造promise函数中形参的顺序。
  1. function rand(m, n) {
  2.     return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
  3. }
  4. // const btn = document.querySelector('#btn');
  5. // btn.addEventListener('click', function(){
  6. //     setTimeout( () => {
  7. //         let n = rand(1,100);
  8. //         console.log(n);
  9. //         if(n <= 30){
  10. //             alert('恭喜你中了10万rmb大奖!!!');
  11. //         }
  12. //         else{
  13. //             alert('再接再厉');
  14. //         }
  15. //     },1000);
  16. // })
  17. // promise改写这段代码
  18. const p = new Promise((resolve, reject) => {
  19.     setTimeout(() => {
  20.         let n = rand(1, 100);
  21.         console.log(n);
  22.         if (n <= 30) {
  23.             resolve(n);
  24.         }
  25.         else {
  26.             reject(n);
  27.         }
  28.     }, 1000)
  29. });
  30. p.then((result) => {
  31.     alert('恭喜你中了10万rmb大奖!!!  ' + result);
  32. }, (error) => {
  33.     alert('再接再厉 ' + error);
  34. })
复制代码
2、fs读文件案例

  1. const fs = require('fs');
  2. // fs.readFile('./text.txt', 'utf-8',(err,data) => {
  3. //     if(err)
  4. //         console.log('读取文件失败');
  5. //     else{
  6. //         console.log(data);
  7. //     }
  8. // })
  9. let p = new Promise((resolve, reject) => {
  10.     fs.readFile('./text.txt', 'utf-8', (err, data) => {
  11.         if (err)
  12.             reject(err);
  13.         else {
  14.             resolve(data);
  15.         }
  16.     });
  17. });
  18. p.then((data) => {
  19.     console.log(data);
  20. }, (err) => {
  21.     console.log('读取文件失败');
  22. });
复制代码
3、AJAX请求

  1. var btn = document.querySelector('button');
  2. btn.addEventListener('click', function () {
  3.     const p = new Promise((resolve, reject) => {
  4.         const xhr = new XMLHttpRequest();
  5.         xhr.open('GET', 'http://www.baidu.com');
  6.         xhr.send();
  7.         xhr.onreadystatechange = function () {
  8.             if (xhr.readyState === 4) {
  9.                 if (xhr.status >= 200 && xhr.status < 300) {
  10.                     resolve(xhr.response);
  11.                 } else {
  12.                     reject(xhr.status);
  13.                 }
  14.             }
  15.         }
  16.     });
  17.     p.then(value => {
  18.         console, log(value);
  19.     }, reason => {
  20.         console.warn(reason);
  21.     })
  22. })
复制代码
4、函数封装promise读文件fs

  1. function mineReadFile(path) {
  2.     return new Promise((resolve, reject) => {
  3.         require('fs').readFile(path, 'utf-8', (err, data) => {
  4.             if (err) reject(err);
  5.             resolve(data);
  6.         })
  7.     })
  8. }
  9. mineReadFile('./成绩-ok.txt').then((value) => {
  10.     console.log(value);
  11. }, (reason) => {
  12.     console.log(reason);
  13. });
复制代码
5、util.promise方法举行promise风格转化

        node.js中的util有一个promisify方法,引入util模块后就可以使用。
util.promisify(original):官方定义——传入一个遵循常见的错误优先的回调风格的函数,即以(err, value)=>...回调,并返回一个promise版本。
这个方法的原理应该就是 2.4 的封装,不过要把内部函数作为参数传进去。
  1. const util = require('util');
  2. const fs = require('fs');
  3. let mineReadFile = util.promisify(fs.readFile);
  4. mineReadFile('./1.txt').then(() => { }, () => { });
复制代码
6、封装AJAX请求

        Ajax请求要导入jQuery
  1. function sendAJAX(url) {
  2.     return new Promise((resolve, reject) => {
  3.         const xhr = new XMLHttpRequest();
  4.         xhr.open('GET', url);
  5.         xhr.send();
  6.         xhr.onreadystatechange = function () {
  7.             if (xhr.readyState === 4) {
  8.                 if (xhr.status >= 200 && xhr.status < 300) {
  9.                     resolve(xhr);
  10.                 }
  11.                 else {
  12.                     reject(xhr.status);
  13.                 }
  14.             }
  15.         }
  16.     })
  17. }
  18. sendAJAX('www.baidu.com').then(value => {
  19.     console.log(value);
  20. }, warn => {
  21.     console.log(warn);
  22. })
复制代码
三、Promise对象状态属性

        打印promise对象实例,可以看到有一个属性叫PromiseState,意思是这个实例对象的状态。实例状态(PromiseState)有三种:
- pending:未决定的
- resolve等同于fulfilled:成功
- rejected:失败
        状态只能由pending到别的两种的转换,一旦成功和失败,就不会再改变,所以成功和失败是不可能相互转换的
四、promise对象效果值属性

        promise对象实例还有个属性叫效果值PromiseResult,存放的是对象成功或失败的效果,其实就是reject和resolve函数的传参,我们把效果作为参数传递了。
五、Promise的API

1、Promise构造函数:Promise (executor) { }

        (1) executor函数:执行器,(resolve, reject) => { }
        (2) resolve函数:内部定义成功时我们调用的函数 value => { }
        (3) reject函数:内部定义失败时我们调用的函数 reason => { }
executor 会在Promise内部立刻同步调用,异步操作会在执行器中执行
2、Promise.prototype.then方法

指定成功/失败的回调函数,定义在原型上,(onResolve, onReject) => {} :
        (1) onResolve函数,成功的回调函数 value => { }
        (2) onReject函数,失败的回调函数 reason => { }
        (3) 返回一个新的promise对象
3、Promise.prototype.catch方法

只能指定失败的回调,(onReject) => {}
4、Promise.resolve 方法 

                (value) => {}
        (1) value:成功的数据或者promise对象
        (2) 返回一个成功的promise对象
5、Promise.reject方法

                (reason) => {}
        (1) reason失败的原因
        (2) 返回一个失败的对象
6、Promise.all方法

                (promises) => {}
        (1) promises:包含n个promise的数组
        (2) 阐明:返回一个新的promise,只有promises都成功才成功,返回全部的成功效果;只要有一个失败就直接失败,所以多个失败也只返回第一个失败的效果
7、Promise.race方法

                (promises) => {}
        (1) promises:包含n个promise的数组
        (2) 返回效果由第一个执行完毕产生效果的promise决定,相当于多个promise竞速
六、Promise的几个关键问题

1、怎样改变Promise的状态?

  1. let p = new Promise((resolve, reject) => {
  2.     // 只能三选一
  3.     // 1. resolve函数,由pending => fulfilled(resolved)
  4.     resolve('ok');
  5.     // 2. reject函数,由pending => rejected
  6.     reject('error');
  7.     // 3. 抛出错误
  8.     throw '出问题了';
  9. })
复制代码
2、一个Promise指定多个成功/失败回调函数,都会调用!

3、改变Promise状态和指定回调函数谁先谁后?

都有可能,看具体指定回调和改变状态的执行环境;一样平常来说如果promise内部改变状态的函数是同步执行,那就是先改变状态,但是如果是个异步函数,好比说定时器,当指定回调已经完成时定时器还没有执行,那就是指定回调先了。
4、promise.then()返回的新promise的效果状态由什么决定?

简单来说:由then()指定的回调函数执行的效果决定
详细解释:
(1) 如果是抛出非常throw,新promise变为rejected,reason()抛出非常
(2) 如果返回的是非promise的恣意值,新promise变为resolved,value为返回值(不论是resolve还是reject的返回)
(3) 如果返回的是一个新的promise,此promise的效果就会成为新promise的效果
5、promise怎样串联多个操作使命

  1. let p = new Promise((resolve, reject) => {
  2.     setTimeout(() => {
  3.         resolve('ok');
  4.     }, 2000)
  5. })
  6. p.then(value => {
  7.     // 第一个then
  8.     return new Promise((resolve, reject) => {
  9.         resolve(value)
  10.     })
  11. }, reason => { }).then(value => {
  12.     // 第二个then
  13.     console.log(value);
  14. }).then(value => {
  15.     // 第三个then
  16.     console.log(value);
  17. })
复制代码
输出:ok undefined
分析:计时器时间一到,会改变p的状态为成功,p在这之前通过then(第一个)指定了成功的回调函数;这个回调函数的返回值是一个新的promise,且新promise的状态也通过resolve指定为成功,通过新函数的then(第二个)指定了它的回调函数为打印ok;新函数的then(第二个)也会返回一个新的promise,但是这里then(第二个)的返回值并不是回调函数,什么也没有返回,也就是undefined,所以新新promise状态为成功,then(第三个)执行时没有任何传参,value为undefined。
6、promise非常穿透?

当使用promise链式调用时,可以在最后指定失败的回调
前面任何操作出现了非常,都会传到最后失败的回调中
明白:某一层失败,那么就会执行失败的回调,抛出非常,这样新的promise就也是失败,新的promise失败,新新promise也失败,一层层抛出,直至最后的失败回调,中心层的promise没有写失败回调,不做任何处理。
7、promise链怎么中断?

七、手写Promise

  1. // 用类封装Promise
  2. class Promise {
  3.     // Promise构造方法,接收一个函数作为参数,
  4.     // 这个函数就是使用时用来改变Promise对象状态的函数,所以这个函数传入Promise后直接调用
  5.     // exeutor传入了两个函数参数,就是resolve和reject, 在同步执行时会按照情况调用
  6.     constructor(exeutor) {
  7.         this.promiseState = 'pendding';
  8.         this.promiseResult = null;
  9.         // callback用来保存回调
  10.         // 异步任务时,会先执行then,结果为pendding,
  11.         // 等到异步执行时,改变了状态,但是then已经执行过了,那么对应的回调函数(使用者自己设置的)就不能再被调用了,
  12.         // 而且exeutor并不知道使用者设置的回调函数内容
  13.         // 那么此时,用callback来保存这两个函数的内容,在异步执行时,就可以通过callback来调用这两个回调函数
  14.         // 在resolve和rejected两个改变状态的函数被调用时,判断callback有无内容,
  15.         // 有内容就说明then先执行了,为异步任务,使用callback调用
  16.         // 有多个回调,用数组来存储,每个位置存一个对象,每个对象里存两个回调函数
  17.         this.callback = [];
  18.         const that = this;
  19.         function resolve(data) {
  20.             // resolve函数的任务
  21.             // 1. 修改对象的状态(promiseState)
  22.             // 2. 设置对象结果值(promiseResult)
  23.             // 状态只能改变一次
  24.             if (that.promiseState !== 'pendding') return;
  25.             // 这两个值都是属于实例对象的,打印实例对象可以看到,所以用this来指向
  26.             // 注意:resolve函数不是实例对象调用的,而是window
  27.             // 所以要提前存好this,在这里才能正确指向实例对象的属性;
  28.             // 或者使用箭头函数(箭头函数没有this,所以如果用到this就是定义它的对象) let resolve = (data) => {}
  29.             that.promiseState = 'fulfilled';
  30.             that.promiseResult = data;
  31.             // 当改变状态的函数是异步任务时,会先执行then,再改变状态,那么这个时候回调函数就是在改变状态后执行
  32.             // 这个时候需要拿到回调函数,用callback来保存这几个回调函数,然后在这里调用callback,
  33.             // 否则就算状态改变了回调函数也不会再被执行了
  34.             if (that.callback.onResolve) {
  35.                 // 要注意回调函数都是异步执行的哦
  36.                 setTimeout(() => {
  37.                     that.callback.forEach(item => {
  38.                         item.onResolve;
  39.                     })
  40.                 });
  41.             }
  42.         }
  43.         function reject(data) {
  44.             // 状态只能改变一次
  45.             if (that.promiseState !== 'pendding') return;
  46.             that.promiseState = 'rejected';
  47.             that.promiseResult = data;
  48.             if (that.callback.onReject) {
  49.                 setTimeout(() => {
  50.                     that.callback.forEach(item => {
  51.                         item.onReject;
  52.                     })
  53.                 })
  54.             }
  55.         }
  56.         // 注意:抛出异常也是可以改变对象状态的
  57.         try {
  58.             exeutor(resolve, reject)(); // 同步执行传入的参数
  59.         }
  60.         catch (e) {
  61.             reject(e);
  62.         }
  63.     }
  64.     // 添加then方法,传入了两个函数作为参数
  65.     then(onResolve, onReject) {
  66.         // 异常穿透处理
  67.         // 如果函数没有写失败回调函数,当函数失败时,就会直接报错,
  68.         // 所以我们让没传入的失败回调默认为继续向下抛出错误就行,这样下一个promise也因为throw状态为失败
  69.         if (typeof onReject !== 'function') {
  70.             onReject = reason => {
  71.                 throw reason;
  72.             }
  73.         }
  74.         // 基于异常穿透,扩展一下,让成功回调函数没传入时也有一个默认内容,增强容错性
  75.         // 默认为返回成功值。
  76.         if (typeof onResolve !== 'function') {
  77.             onResolve = value => value; //简写,return value
  78.         }
  79.         // then方法返回一个新的promise,新promise的状态由then执行的结果的返回值决定
  80.         // 用return返回一个Promise对象
  81.         return new Promise((resolve, reject) => {
  82.             const self = this;
  83.             // 把这个根据执行结果返回新Promise的函数封装起来,在then的具体指定回调时直接调用,
  84.             // 同步、异步都要用,反正只要执行回调函数的地方都要用到
  85.             // 让指定回调和返回同时执行
  86.             // 这里用箭头函数的话内部用this是没有问题的
  87.             let callback = (type) => {
  88.                 // 抛出异常的话,新Promise就是失败状态
  89.                 try {
  90.                     // 用result拿到当前promise的执行结果,不管当前promise的状态是什么,
  91.                     // 新promise只看它执行后的结果,最主要就是关注它的回调函数是不是又是一个promise
  92.                     // 这句话就是既执行回调又将结果保存起来
  93.                     let result = type(self.promiseResult);
  94.                     if (result instanceof Promise) {
  95.                         // 如果使用者嵌套了Promise,那么result就是这个Promise, 那么新Promise的状态由这个promise的状态来决定
  96.                         // 使用它的then方法指定新函数的状态,
  97.                         // 注意,一般来说then都是指定自己的回调函数,但是这里传进来的使用者Promise相当于返回给了新Promise,所以只要改变状态,也只能改变状态,
  98.                         // 使用者传进来的Promise的then不需要在这里改,改新函数就行了,否则会嵌套太多回调
  99.                         result.then(v => { resolve(v) }, r => { reject(r) })
  100.                     }
  101.                     else {
  102.                         // 如果返回的不是Promise,那么它的状态一定是成功,调用对应函数改变状态。
  103.                         resolve(result);
  104.                     }
  105.                 } catch (e) {
  106.                     // 抛出异常
  107.                     reject(e);
  108.                 }
  109.             }
  110.             // 状态为成功时调用onResolve,失败时调用onReject
  111.             // 设置回调函数是异步的(then异步),用setTimeout设置异步
  112.             if (this.promiseState === 'fulfilled') {
  113.                 setTimeout(() => {
  114.                     callback(onResolve);
  115.                 })
  116.             }
  117.             if (this.promiseState === 'rejected') {
  118.                 setTimeout(() => {
  119.                     callback(onReject);
  120.                 })
  121.             }
  122.             if (this.promiseState === 'pendding') {
  123.                 // callback是一个数组,then回调函数可以设置多个,都可以执行
  124.                 this.callback.push({
  125.                     // 异步要返回执行结果也可以调用callback,只要把它用function包起来,回调函数就可以正常被调用了
  126.                     onResolve: function () {
  127.                         callback(onResolve);
  128.                     },
  129.                     onReject: function () {
  130.                         callback(onReject);
  131.                     }
  132.                 })
  133.             }
  134.         })
  135.     }
  136.     // catch方法添加,传入一个函数参数作为失败回调,只指定失败回调
  137.     // 前面已经写好了then,其实就是只给promise指定失败回调,resolve设置为undefined就行
  138.     // 另外还要实现异常穿透,需要在then的抛出异常中设置允许reject没有定义的情况,这种情况下就设置reject为继续抛出异常
  139.     // 事实上,没传onResolve的时候也应该考虑,有些情况就是不需要
  140.     // 这样写完以后,这一层Promise没传回调函数,它的下一层Promise仍然能根据它的构造函数的改变状态情况得到自己的状态
  141.     catch(onReject) {
  142.         return taht.then(undefined, onReject);
  143.     }
  144.     // resolve方法,它是属于Promise,不是属于实例对象的,静态函数
  145.     // 这个方法的目的是快速获得一个成功的对象
  146.     static resolve = (value) => {
  147.         return new Promise((resolve, reject) => {
  148.             if (value instanceof Promise) {
  149.                 value.then((v) => {
  150.                     resolve(v);
  151.                 }, (r) => {
  152.                     reject(r);
  153.                 })
  154.             }
  155.             else {
  156.                 resolve(value);
  157.             }
  158.         })
  159.     }
  160.     // reject方法封装,不管Promise状态如何改变,属于Promise,静态函数
  161.     // 快速获得一个失败的对象
  162.     static reject(reason) {
  163.         return new Promise((resolve, reject) => {
  164.             reject(reason);
  165.         })
  166.     }
  167.     // all方法封装
  168.     //传入多个Promise对象,都成功新promise才成功,
  169.     // 如果成功,就把这个回调函数先存起来,要不然新Promise就直接成功了,后面要是失败了就不能改变它的状态了
  170.     // 如果失败,直接执行
  171.     static all(promises) {
  172.         return new Promise((resolve, reject) => {
  173.             let count = 0;
  174.             let arr = [];
  175.             for (let i = 0; i < promises.length; i++) {
  176.                 promises[i].then(v => {
  177.                     count++;
  178.                     arr[i] = v;
  179.                     if (count === promises.length) {
  180.                         resolve(arr);
  181.                     }
  182.                 }, r => {
  183.                     reject(r)
  184.                 })
  185.             }
  186.         })
  187.     }
  188.     // race方法封装
  189.     // 由最先执行完改变状态的promise决定,直接让每一个promise的then都指定新Promise状态就行了
  190.     static race(promises) {
  191.         return new Promise((resolve, reject) => {
  192.             for (let i = 0; i < promises.length; i++) {
  193.                 promises[i].then(v => {
  194.                     resolve(v);
  195.                 }, r => {
  196.                     reject(r);
  197.                 })
  198.             }
  199.         })
  200.     }
  201. }
复制代码
八、async函数

  1. async function main() {
  2.     return 111;
  3. }
  4. let result = main();
  5. // result.promiseState = 'fulfilled'
复制代码
九、await表达式

1、await右侧的表达式一样平常为promise对象,但也可以是其他的值
2、如果await右侧是promise对象,那么await返回的是promise成功的值
3、如果表达式是其他值,直接将此值作为await的返回值
4、注意:await只能写在async函数中;如果await的promise失败了,就会抛出非常,必要try…catch捕获处理
  1. async function main() {
  2.     let p = new Promise((resolve, reject) => {
  3.         resolve('ok');
  4.     })
  5.     let q = new Promise((resolve, reject) => {
  6.         reject('no');
  7.     })
  8.     // 1.右侧为promise对象
  9.     let res1 = await p;
  10.     console.log(res1);
  11.     // 2.promise是失败的
  12.     try {
  13.         let res2 = await q;
  14.     } catch (e) {
  15.         console.log(e)
  16.     }
  17. }
  18. main();
复制代码

文章摘自 web前端学习条记之promise_前端promise-CSDN博客  ,并非本博主原创,在学习过程中重录加深印象!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4