promise的学习
为了解决回调地狱的问题,所以出现了promise的设计思想。
#### promise A+ 规范
[https://promisesaplus.com/](https://promisesaplus.com/)
#### 几点需要强调的。
- promise 必须有一个then方法。
- promise 必须有三个状态。"PENDING" "FULFILLED" "REJECTED" 。状态一旦改变,不可回滚。
- promise 可以链式调用。promise 可以穿透(不写resolve方法或者是一个常量也行)。
- promise 返回一个新的promise。
- 捕获错误机制。找不到就向下找。
#### 手写一个promise的思路。总体就是数组保存所有的then里注册的函数。然后等时机到了一个个执行。
(1) 先有一个类,有then方法。status有三个变量pending,fulfilled,rejected。
(2) 成功的变量value和失败的变量reason,保存成功回调onResolvedCallbacks,保存失败的回调onRejectedCallbacks
(3) 主体是then函数的递归,链式调用所有then返回一个promise。但是为了解决then函数的返回普通值和then穿透的问题。
#### 如何证明promise和x重复的问题。
##### resolvePromise 中的 called 表示害怕有的人的promise写的不规范,resolve之后还能reject,所以加了called进行了限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | const PENDING = "PENDING" ; const SUCCESS = "FULFILLED" ; const FAIL = "REJECTED" ; // 严谨 应该判断 别人的promise 如果失败了就不能在调用成功 如果成功了不能在调用失败 function resolvePromise(promise2, x,resolve,reject) { if (promise2 === x){ return reject( new TypeError( 'TypeError: Chaining cycle detected for promise #<Promise>' )); } let called; if ( typeof x === 'function' || ( typeof x === 'object' && x != null )){ try { let then = x.then; // then 可能是getter object.defineProperty if ( typeof then === 'function' ){ // {then:null} then.call(x,y=>{ if (called) return ; // 1) called = true ; resolvePromise(promise2,y,resolve,reject); },r=>{ if (called) return ; // 2) called = true ; reject(r); }) } else { resolve(x); } } catch (e){ if (called) return ; // 3) 为了辨别这个promise 不能调用多次 called = true ; reject(e); } } else { resolve(x); } } class Promise { constructor(executor) { this .status = PENDING; this .value = undefined; this .reason = undefined; this .onResolvedCallbacks = []; this .onRejectedCallbacks = []; const resolve = value => { if ( this .status === PENDING) { this .value = value; this .status = SUCCESS; this .onResolvedCallbacks.forEach(fn => fn()); } }; const reject = reason => { if ( this .status === PENDING) { this .reason = reason; this .status = FAIL; this .onRejectedCallbacks.forEach(fn => fn()); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { // .catch(function(){}) .then(null,function) onFulfilled = typeof onFulfilled === 'function' ?onFulfilled:val=>val; onRejected = typeof onRejected === 'function' ?onRejected:err=>{ throw err} let promise2; promise2 = new Promise((resolve, reject) => { if ( this .status === SUCCESS) { setTimeout(() => { try { let x = onFulfilled( this .value); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); } if ( this .status === FAIL) { setTimeout(() => { try { let x = onRejected( this .reason); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); } if ( this .status === PENDING) { this .onResolvedCallbacks.push(()=>{ setTimeout(() => { try { let x = onFulfilled( this .value); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); }); this .onRejectedCallbacks.push(()=> { setTimeout(() => { try { let x = onRejected( this .reason); resolvePromise(promise2, x, resolve, reject); } catch (err) { reject(err); } }); }); } }); return promise2; } } // 希望测试一下这个库是否符合我们的promise A+规范 // promises-aplus-tests Promise.defer = Promise.deferred = function (){ let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } module.exports = Promise; // npm i promises-aplus-tests -g // promise 相关方法 // generator |
promise的三种状态:
- pending 等待状态
- resolved 完成状态
- rejected 拒绝状态
promise的三种状态,只能是pending->resolved或者pending->rejected,不能有其他类型的状态转换,并且状态一旦发生转换,就不再发生变化。
promise的方法:
1.then
2.all
3.race
上例子吧 :
例子1:在异步的读取完a1,a2,a3,a4,a5的文件之后,执行一个总的方法。
1 'use strict'; 2 //这是一个简单的应用 3 //要求:在异步读完a1.txt,a2.txt,a3.txt,a4.txt的时候执行一个总的方法 4 var Promise = require('bluebird'); 5 var fs = require("fs") ; 6 7 8 var promises = []; 9 10 //Promise all方法的使用 11 for(var i = 1; i < 5; i++){ 12 var promise = new Promise(function(resolve,reject){ 13 fs.readFile("a" + i + ".txt","utf8",function (error,data){ 14 if(error){ 15 reject(error) 16 }else{ 17 resolve(data) 18 } 19 }) ; 20 }); 21 promises.push(promise); 22 } 23 24 Promise.all(promises).then(function(data){ 25 console.log(data); 26 }).catch(function(e){ 27 console.log(e); 28 });
//
[ 'this is a1 !', 'this is a2 !', 'this is a3 !', 'this is a4 !' ]
例子2:在异步的读取a1,a2,a3,a4,a5的文件之后,只要有一个文件读取完成,就执行最后的方法。
1 "use strict"; 2 3 var Promise = require("bluebird"); 4 var fs = require("fs"); 5 6 var promises = []; 7 8 //Promise race方法的使用 9 for(var i = 1; i < 5; i++){ 10 var promise = new Promise(function(resolve,reject){ 11 fs.readFile("a" + i + ".txt","utf8",function (error,data){ 12 if(error){ 13 reject(error) 14 }else{ 15 resolve(data) 16 } 17 }) ; 18 }); 19 promises.push(promise); 20 } 21 Promise.race(promises).then(function(data){ 22 console.log(data); 23 }).catch(function(e){ 24 console.log(e); 25 })
//this is a1 !
例子3:要求:在读完a1.txt的时候读a2.txt,在读完a2.txt,读a3,然后a4.
1 var a1 = new Promise(function(resolve,reject){ 2 fs.readFile('a1.txt','utf8',function(error,data){ 3 if(error){ 4 reject(error); 5 }else{ 6 resolve(data); 7 } 8 }); 9 }); 10 var ss = a1.then(function(){ 11 return new Promise(function(resolve,reject){ 12 fs.readFile('a2.txt','utf8',function(error,data){ 13 if(error){ 14 reject(error); 15 }else{ 16 resolve(data); 17 } 18 }); 19 }); 20 }).then(function(){ 21 return 'yanjinyun'; 22 /*return new Promise(function(resolve,reject){ 23 fs.readFile('a3.txt','utf8',function(error,data){ 24 if(error){ 25 reject(error); 26 }else{ 27 resolve(data); 28 } 29 }); 30 });*/ 31 }).then(function(data){ 32 return new Promise(function(resolve,reject){ 33 fs.readFile('a4.txt','utf8',function(error,data){ 34 if(error){ 35 reject(error); 36 }else{ 37 resolve(data); 38 } 39 }); 40 }); 41 }).catch(function(e){ 42 43 });
#### 第一层的then和第二层的then
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | let aaa = new Promise( function (resolve, reject) { setTimeout(() => { resolve(132) },100) }); aaa.then(data => { console.log( '--->>>>1' , data) return 'then1' }).then(data => { console.log( '--->>>>3' , data) return 'then3' }); aaa.then(data => { console.log( '--->>>>2' , data) }); // 结果 --->>>>1 132 --->>>>2 132 --->>>>3 then1 |
#### new Promise的时候promise里边的就执行了。
#### then如果不返回一个值的,默认返回undefined,算一个普通值。
let aaa = new Promise(function(resolve, reject) { setTimeout(() => { resolve(132) },100) }); aaa.then(data => { console.log('--->>>>1', data) }).then(data => { console.log('--->>>>3', data) return 'then3' }); aaa.then(data => { console.log('--->>>>2', data) });
#### 只要有一个reject函数。后边的继续resolve。除非这个reject函数 报错。才会被catch到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | let aaa = new Promise( function (resolve, reject) { setTimeout(() => { resolve(132) },100) }); aaa.then(data => { console.log( '--->>>>1' , data) throw new Error(); }, err => { console.log( '进入---err1' ) return 'err1' ; }).then(data => { console.log( '--->>>>3' , data) }, err => { console.log( '进入---err3' ) // return 'err3'; // 写return 和 不写 return的区别在于 下边的resolve中接收到的值是不是undefined throw new Error(); }).then(data => { console.log( '--->>>>4' , data) }, err => { console.log( '进入---err4' ) return 'err4' ; }).then(data => { console.log( '--->>>>5' , data) }, err => { console.log( '进入---err5' ) return 'err5' ; }). catch (e => { console.log(e + '---' ); }); aaa.then(data => { console.log( '--->>>>2' , data) }); |
#### resolve(new Error('error')) 结果也是正常执行的。
##### promise的defer实现。
1 2 3 4 5 6 7 8 | Promise.defer = Promise.deferred = function (){ let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 没用defer之前 function read(url) { return new Promise((resolve, reject) => { fs.readFile(url, 'utf-8' , (err, data) => { if (err) reject(err); resolve(data); }) }) } // 用了defer之后 function read(url) { let defer = Promise.defer(); fs.readFile(url, 'utf-8' , (err, data) => { if (err) defer.reject(err); defer.resolve(data); }) return defer.promise; } |
#### race 只要有一个失败了就失败了
// 测试只要有一个race 中只要有一个promise reject了,那么久reject了。 let aa = () => { return new Promise(function(resolve, reject) { setTimeout(() => { reject(1); },1000) }) } let bb = () => { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(2); },2000) }) } Promise.race([aa(), bb()]).then(data => { console.log(data); }).catch(e => { console.log('e', e); })
// e 1
#### generator
### promisify 的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function promisify(fn){ // node中 util模块自带了这个功能 return function (...args){ // args = [name,'utf8'] return new Promise((resolve,reject)=>{ fn(...args, function (err,data){ if (err) reject(err); resolve(data); }); // fs.readFile('name.txt','utf8); }) } } // let readFile = promisify(fs.readFile); Promise.all([1,fs.readFile('./name.txt ',' utf8 '),fs.readFile(' ./age.txt ',' utf8')]).then(data=>{ return 100 },err=>{ console.log(err); }).then(data=>{ }); |
#### promise 的 finally
- 无论前边是catch还是finally都是执行。
- 只要前边的catch捕获到了错误,后边的then仍然会执行。后边的then的值是catch的返回值。
- finally 并不会捕获错误,如果发生错误finally前边没有捕获的话,finally仍然会执行,后边的catch仍然会去捕获。
- finallly 像一个不会产生返回值的中间器。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步