promise 概念
ES6 原生支持Promise
Promise是一个构造函数,包含all,reject,resolve等方法。一个Promise用来传递异步操作的消息,它代表了未来才会知道结果的事件。
本质上是一种异步编程的抽象,是返回值或者抛出exception的代理对象。它有一个then方法,任何人都可以访问then来消费这个返回值或exception的。
三种状态
promise只有三种状态,并且状态转移只能是pending -> fulfiled 或者pending -> rejected
pending: 待定状态,Pormise对象刚被初始化的状态
fulfilled: 完成状态,承诺被完成的状态
rejected: 拒绝状态,承诺完成失败的状态
relosve和reject: resolve() 和 reject()是 Promise的两个闭包函数。
通常,读取一个文本,我们会这么写。
"use strict"
const fs = require('fs');
fs.readFile('test.txt', (err, data) => {
if(err){
console.error(err);
return;
}
console.log(data);
});
接下来我们来封装类似promise风格的函数,如下:
"use strict"
const fs = require('fs');
let proReadFile = (filename) => {
return new Promise( (resolve, reject) => {
fs.readFile(filename, (err, data) => {
if(err){
reject(err);
return;
}
resolve(data);
});
});
};
proReadFile('text.txt').then(console.log).catch(console.error);
这里把fs.readFile封装成一个promise如果读入正常就利用then把数据交给console.log函数消费,如果失败就利用catch把err交给console.error消费
分析代码
源码 一个基本的MyPromise
//函数构造器
function MyPromise(exe) {
//三种状态,默认"pending"
this.status = "pending";
//如果状态是rejected, reason 的值非空,带给消费者消费
this.reason = null;
//如果状态是fulfilled, result 的值非空,带给消费者消费
this.result = null;
//resolve回调函数数组,当exe执行器执行完,触发该函数数组,而后修改状态为fulfilled
this.resolveArr = [];
//reject回调函数数组,当exe执行器执行完,触发该函数数组,而后修改状态为rejected
this.rejectArr = [];
//保证各个函数里面的变量是该对象的属性
let self = this;
//promise留给外部的闭包函数,依次触发resolveArr里面的函数
let resolve = () => {
if("pending" != self.status) {
return ;
}
//setTimeout异步的目的,是为了让then的方法先执行,将消费函数先注册上
//否则resolve执行完,then里面的函数并不会执行
setTimeout( () => {
let fn = null;
let result = self.result;
while(fn = self.resolveArr.shift()) {
result = fn(result);
}
self.result = result;
self.status = "fulfilled";
});
}
//同理如上
let reject = () => {
if("pending" != self.status) {
return ;
}
setTimeout( ()=> {
let fn = null;
let reason = self.reason;
while(fn = self.resolveArr.shift()) {
reason = fn(reason);
}
self.reason = reason;
self.status = "rejected";
});
}
exe(resolve, reject);
}
再来看看then链式结构
MyPromise.prototype.then = (onFulfilled, onRejeceted) => {
let self = this;
//最终返回promise对象,保证thenable
return new MyPromise( (resolve, reject) => {
//当promise.status 为"resolved", 成功执行下一个链式
function cb(result) {
let ret = ("function" == typeof onFulfilled && onFulfilled(result)) || result;
if(ret["then"]) {
ret.then( (result)=>{
resolve(result);
}, (reason)=>{
reject(reason);
});
} else {
resolve(ret);
}
}
//当promise.status 为"rejected", reject就把reason传递给执行下一个链式
function errcb(reason) {
let ret = ("function" == typeof onRejeceted && onRejeceted(reason)) || reason;
reject(reason);
}
if("pending" == self.status) {
self.resolveArr.push(cb);
self.rejectArr.push(errcb);
} else if ("rejected" == self.status) {
errcb(self.reason);
} else if ("resolved" == self.status) {
cb(self.result);
}
});
}
现在看看resolve函数,reject同理:
- MyPromise的构造函数,new出新的promise对象P1,会立即执行使用者传递的执行器exe,当exe运行成功,便会触发resolve函数,并把数据交给resolve函数。但由于resolve里面的setTimouet不会立即触发resolveArr里面函数。这时候resolveArr里面还是空的(假设exe里面从执行到触发resolve都是同步执行的)。
- resolveArr里面的函数是怎么来的,在then里面构造promise对象P2时,从then里面获取的: self.resolveArr.push(cb)。这时候,只是把cb不停地塞进resolveArr里面,但不会立即执行。
- 直到P1的resolve里面的异步setTimeout超时,匿名函数执行。不停地弹出resolveArr里面的函数,这里面的函数全是cb, cb的参数是P1的result。
- 就拿上叙第一例说明,cb和console.log是什么关系。其实简单一点,console.log就是onFulfilled,会被cb函数在其中间部分调用。先不看if(ret["then"]),先看else,就是把onFulfilled里面结果或者说是P2结果赋值给P2的resolve执行,又是promise的核心。
catch 是 then 的语法糖,类似then(null, reject);