Promise 详解
1、 历史背景:
Javascript是一门单线程语言,所以早期我们解决异步的场景时,大部分情况是通过回调函数来进行。
实例1
var dynamicFunc = (callback)=>{
setTimeout(()=>{
callback();
},500)
}
dynamicFunc(()=>{
console.log("done")
})
// 0.5秒后执行callback函数
// 如果后继还有内容需要异步进行处理的话,就需要多个异步函数进行嵌套,不利于后期维护
setTimeout(()=>{
console.log("1")
setTimeout(()=>{
console.log("2")
},500)
},500)
// 为了能使回调函数以更优雅的方式进行调用,在ES6中js产生了一个名为promise的新规范,
// 他让异步操作编写代码更加像同步代码2、Promise 使用
在支持ES6高级浏览器环境中,我们可以通过new Promise()即可构造一个promise实例
这个构造函数接受一个函数作为参数,这个参数函数接收两个参数,分别是resolve和reject,
代表我们需要改变当前实例的状态到”已完成“或者是”已拒绝“
实例2
const promise1 =()=>{
return new Promise((resolve,reject)=>{
// 定义异步内容
setTimeout(()=>{
console.log("promise1");
// 输出完成后,调用函数传入的resolve函数,将该promise实例标记为已完成,
// 当前promise串行继续执行
resolve();
},500)
})
}
const promise2 = ()=>{
return new Promise((resolve,reject)=>{
console.log("promise2");
resolve();
},1000)
}
// 串行调用:
promise1().then(()=>{
return promise2();
})
// 等价于
promise1().then(promise2)实例3
function promise3(){
return new Promise((resolve,reject)=>{
var random = Math.round(Math.random()*1); 生成 0,1
setTimeout(()=>{
if(random===0){
resolve('resolve')
}
else{
reject('reject')
}
},1000)
})
}
var onResolve = (val)=>{
console.log("resolve",val);
}
var onReject = (val)=>{
console.log('reject',val)
}
// Promise的then可以接受两个函数,第一个参数为resolve后执行,
// 第二个函数为reject后执行
promise3().then(onResolve,onReject);
// 也可以通过.catch 方法拦截状态边为已拒绝时的
promisepromise3().catch(onReject).then(onResolve);
// 也可以通过try catch进行拦截状态变为已拒绝的promise
try{
promise3().then(onResolve);
}
catch(e){
onReject(e)
}3、小结
1、promise有三种状态,分别是:进行中(pending),已完成(fulfilled),已拒绝(rejected),进行中状态可以转变为已完成或者已拒绝状态,已经改变过状态后无法继续修改状态
2、ES6中的Promise构造函数,new Promise(resolve,reject),执行resolve函数的时候状态将变为fulfilled状态,
执行第二个函数的时候状态将变为rejected状态
3、通过.then 方法,即可在上一个promise达到已完成时继续执行下一个函数或者promise。同时通过resolve和reject时传入参数,
即可给下一个函数或Promise传入初始值
4、已拒绝的Promise,后继可以通过.catch方法或者是.then方法的第二个参数或是try catch进行捕获
4、异步封装Promise
实例4// 声明const ajaxAsync =(url)=>{
return new Promise((resolve,reject)=>{
const client = new XMLHttpRequest();
client.open('get',ur);
client.onreadystatechange = ()=>{
if(this.readyState !== 4){
return ;
}
if(this.state===200){
resolve(this.response);
}
else{
reject(new Error(this.statusText))
}
};
client.send();
})
}
// 调用
ajaxAsync('./ajax.json').catch(()=>{
console.log("fail")
}).then(()=>{
console.log("done")
})5、小结
1、我们可以轻松地把任何一个函数或者是异步函数改为promise,尤其是异步函数,改为Promise之后即可进行链式调用,增强可读性
2、将带有回调函数的异步改为promise也很简单,只需在内部实现实例化promise之后,在原来执行回调函数的地方执行对应的更改promise状态的函数即可
6、Promise规范解读
待定
*
*
*
*
7、 Promise构造函数上的静态方法
1、Promise.resolve
返回一个Promise实例,
并将他的状态设置为已完成,
同时将他的结果作为传入Promise实例的值
实例5:
const promise = Promise.resolve("done");
promise.then((val)=>{
console.log("done",val)
})
// done done 2、Promise.reject
返回一个Promise实例,
并将他的状态设置为已拒绝,
同时将他的结果作为原因传入OnRejected函数
实例6
const promise = Promise.reject("fail");
promise.then(null,(val)=>{
console.log("fail",val)
})
// fail fail3、Promise.all
返回一个Promise实例,
接受一个数组,里面含有一个或多个Promise实例,
当所有实例都称为已完成状态时,进入已完成状态,否则进入已拒绝状态
实例7const promise1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("promise1");
resolve("promise1");
},1000)
}
)}
const promise2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("promise2");
resolve("promise2");
},1000)
}
)}
Promise.all([promise1,promise2]).then(()=>{
console.log("all done");
})4、Promise.race
返回一个Promise实例,
接受一个数组,里面含有一个或多个Promise实例,
当有个Promise实例状态改变时,就进入该状态且不可改变。
这里的所有Promise实例为竞争关系,只选择第一个进入改变状态的Promise的值
const promise1 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("promise1");
resolve("promise1");
},1000)
}
)}
const promise2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("promise2");
resolve("promise2");
},1000)
}
)}
Promise.race([promise1,promise2]).then(()=>{
console.log("one done");
})8、实现一个简易的Promise
const isFunction = variable => typeof variable === 'function';
// 定义Promise的三个状态
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECT";
class Promise {
constructor(handle) {
this._status = PENDING;// 当前状态
this._value = undefined;// 当前值
this._fulfilledQueues = [];// 添加成功回调函数队列
this._rejectedQueues = [];// 添加失败回调函数队列
try {
handle(this._resolve.bind(this), this._reject.bind(this));
}
catch (err) {
this._reject(err)
}
}
_resolve(val) {
const run = () => {
if (this._status !== PENDING) {
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error);
}
}
// 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
// 当前Promise的状态才会改变,且状态取决于参数Promise对象的状态
if (val instanceof Promise) {
val.then(value => {
this._value = value;
this._status = FULFILLED;
runFulfilled(value)
}, err => {
this._value = err;
this._status = REJECTED;
runRejected(err)
})
}
else {
this._value = val;
this._status = FULFILLED;
runFulfilled(val)
}
}
}
setTimeout(run, 0)
}
_reject(err) {
if (this._status !== PENDING) return;
// 依次执行失败队列中的函数,并清空队列
const run = () => {
this._status = REJECTED;
this._value = err;
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err);
}
}
// 为了支持同步的Promise,这里采用异步调用
serTimeout(run, 0)
}
then(onFulfilled, onRejected) {
const {
_value,
_status
} = this;
// 返回一个新的Promise
return new Promise((onFulfilled, onRejected) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value);
}
else {
let res = onFulfilled(value);
if (res instanceof Promise) {
// 如果当前回调函数返回Promise对象,必须等待其状态改变后再执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
// 否则会将返回结果直接作为参数,传入下一个then的回调函数
onFulfilledNext(res);
}
}
}
catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
}
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error);
}
else {
let res = onRejected(error);
if (res instanceof Promise) {
// 如果当前回调函数返回Promise对象,必须等待其状态改变后再执行下一个回调
re.then(onFulfilledNext, onRejectedNext)
}
else {
// 否则会将返回结果作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res);
}
}
} catch (error) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
}
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulfilled);
this._rejectedQueues.push(rejected);
break;
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
fulfilled(_value);
break;
case REJECTED:
rejected(_value);
break;
}
})
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
// 如果参数是Promise实例,直接返回这个实例
if (value instanceof Promise) return value;
return new Promise(resolve => resolve(value));
}
static reject(value) {
return new Promise((resolve, reject) => {
reject(value);
})
}
static all(list) {
return new Promise((resolve, reject) => {
let values = [];
let count = 0;
for (let [i, p] of list.entries()) {
// 数组参数如果不是Promise实例,先调用Promise.resolve
this.resolve(p).then(res => {
values[i] = res;
count++;
// 所有状态都变成fulfilled时返回的Promise状态就变成fulfilled
if (count === list.length) {
resolve(values);
}
}, err => {
// 有一个被rejected时返回的Promise状态就变成rejected
rejected(err);
})
}
})
}
static race(list) {
return new Promise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的Promise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
rejected(err)
})
}
})
}
}
9、Javascript 中的模块化
一、模块化实现的功能
1、每个模块都要有自己的变量作用域,两个模块之间的内部变量不会产生冲突;
2、不同模块之间保留相互导入和导出的方式方法,模块之间能够相互通信。
模块的执行与加载遵循一定的规范,能保证彼此之间的依赖关系
二、CommonJS
每个JS文件就是一个模块,每个模块内部是可以使用require函数和module.exports对象来对模块进行导入和导出的
实例:
const moduleA = require("./moduleA");
module.exports = moduleA;
优点:
1、模块之间内部即使有相同变量名,他们运行时没有冲突。
这说明他有处理模块变量作用域的能力。
2、通过require函数和module.exports对象能实现模块的导入导出,
说明他有导入导出模块的方式,同时能够处理基本的依赖关系
3、在不同模块调用的某个模块,得到相同的结果,说明他保证了模块单例
三、 AMD
Asynchronous module definition 异步模块定义
// moduleA
define((require)=>{
const m = require("moduleB");
})
// moduleB
define((require)=>{
return 'moduleB'
})
// index.js
require(['moduleA','moduleB'],(moduleA,moduleB)=>{
})
四、 UMD Universal Module Definition
作为一个同构的模块化解决方案出现,他能让我们只需在一个地方定义模块内容,
并同时兼容AMD和CommonJS语法
五、ES Module规范
CommonJS规范和AMD规范特点:
1、语言上层的运行环境中实现模块化规范,模块化规范由环境自己定义;
2、相互之间不能共用模块。例如不能在Node.js运行AMD模块,不能直接在浏览器运行Commonjs模块