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进行了限制。
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
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到。
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实现。
Promise.defer = Promise.deferred = function(){ let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
// 没用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 的用法
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 像一个不会产生返回值的中间器。