JS语言的执行环境是“单线程”的,即指一次只能完成一件任务;若是有多个任务,那么必须排队,前面一个任务完成,再执行后一个任务,以此类推。这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段Javascript代码长时间运行(好比死循环),致使整个页面卡在这个地方,其余任务没法执行。node
为了解决这个问题,Javascript语言将任务的执行模式分红两种:同步(Synchronous)和异步(Asynchronous)。git
"同步模式"就是上一段的模式,后一个任务等待前一个任务结束,而后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;"异步模式"则彻底不一样,每个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,因此程序的执行顺序与任务的排列顺序是不一致的、异步的。es6
"异步模式"很是重要。在浏览器端,耗时很长的操做都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操做。在服务器端,"异步模式"甚至是惟一的模式,由于执行环境是单线程的,若是容许同步执行全部http请求,服务器性能会急剧降低,很快就会失去响应。github
本文着重讲ES6的Promise对象的定义和用法 阮一峰老师的ES6详解 - Promise对象 相信你们在学习ES6的过程当中都或多或少的学习过阮老师的ES6教程,那么这里简单举一些例子讲述Promise对象的特色和使用方法npm
ES6提供Promise构造函数,咱们创造一个Promise实例,Promise构造函数接收一个函数做为参数,这个传入的函数有两个参数,分别是两个函数 resolve
和reject
做用是,resolve
将Promise的状态由未成功变为成功,将异步操做的结果做为参数传递过去;类似的是reject
则将状态由未失败转变为失败,在异步操做失败时调用,将异步操做报出的错误做为参数传递过去。
实例建立完成后,可使用then
方法分别指定成功或失败的回调函数,比起f1(f2(f3))的层层嵌套的回调函数写法,链式调用的写法更为美观易读编程
let promise = new Promise((resolve, reject)=>{
reject("拒绝了");
});
promise.then((data)=>{
console.log('success' + data);
}, (error)=>{
console.log(error)
});
执行结果:"拒绝了"
复制代码
let promise = new Promise((resolve, reject)=>{
reject("拒绝了");
resolve("又经过了");
});
promise.then((data)=>{
console.log('success' + data);
}, (error)=>{
console.log(error)
});
执行结果: "拒绝了"
复制代码
上述代码不会再执行resolve的方法数组
then
方法下一次的输入须要上一次的输出then
中then
中返回的不是Promise对象而是一个普通值,则会将这个结果做为下次then的成功的结果then
中失败了 会走下一个then
的失败then
中不写方法则值会穿透,传入下一个then
中用node fs模块读取文件的流程来测试 咱们建立一个读取文件的方法,在Promise中定义若是读取成功则展现文件的内容,不然报出错误promise
let fs = require('fs');
function read(file, encoding) {
return new Promise((resolve, reject)=>{
fs.readFile(filePath, encodeing, (err, data)=> {
if (err) reject(err);
resolve(data);
});
})
}
复制代码
因为想看到屡次连贯回调,咱们专门设置3个txt文件,其中1号文件的内容为2号文件的文件名,2号文件的内容为3号文件的文件名,3号中展现最终内容浏览器
执行代码以下:缓存
read('1.promise/readme.txt', 'utf8').then((data)=>{
console.log(data)
});
复制代码
读取一个文件的打印结果为,readme2.txt
咱们改造这个代码,添加多个回调,在最后一个以前的全部then中都return出当前返回的promise对象
read('readme.txt', 'utf8').then((data)=>{
return read(data, 'utf8');
}).then((data)=>{
return read(data, 'utf8')
}).then((data)=>{
console.log(data);
});
最终输出 readme3.txt的内容
复制代码
再对下一步then进行新的处理,咱们对readme3.txt的内容进行加工并返回
read('readme.txt', 'utf8').then((data)=>{
return read(data, 'utf8');
}).then((data)=>{
return read(data, 'utf8')
}).then(data=>{
return data.split('').reverse().join(); // 这一步返回的是一个普通值,普通值在下一个then会做为resolve处理
}).then(null,data=>{ // 特地不对成功作处理,放过这一个值,进入下一步
throw new Error('出错') // 因为上一步返回的是普通值,走成功回调,不会走到这里
}).then(data=>{
console.log(data) // 最终会打印出来上上步的普通值
});
复制代码
这里咱们将内容处理后,则将一个普通值传给了下次的then,可是因为下一个then没有处理成功方法(null)
最后咱们看一下对错误的处理,在处理完readme3.txt的结果后,咱们将这个值传入下一个then中,令其做为一个文件名打开,然而此时已经找不到这个不存在的文件了,那么在最后一步就会打印出报错的结果
read('readme.txt', 'utf8').then((data)=>{
return read(data, 'utf8');
}).then((data)=>{
return read(data, 'utf8')
}).then(data=>{
return data.split('').reverse().join();
}).then(null,data=>{
throw new Error('出错')
}).then(data=>{
return read(data, 'utf8')
}).then(null,(err)=>{
console.log(err)
});
复制代码
结果:
Promises Aplus规范即规定了Promise的原理,源代码规范等,经过这个规范,咱们能够本身实现一个基于PromiseA+规范的Promise类库,下面咱们展现一下源码的实现
/**
* Promise 实现 遵循promise/A+规范
* 官方站: https://promisesaplus.com/
* Promise/A+规范译文:
* https://malcolmyu.github.io/2015/06/12/Promises-A-Plus/#note-4
*/
// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(excutor) {
let self = this; // 缓存当前promise实例对象
self.status = PENDING; // 初始状态
self.value = undefined; // fulfilled状态时 返回的信息
self.reason = undefined; // rejected状态时 拒绝的缘由
self.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
self.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
function resolve(value) { // value成功态时接收的终值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 为何resolve 加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只容许在 execution context 栈仅包含平台代码时运行.
// 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。
setTimeout(() => {
// 调用resolve 回调对应onFulfilled函数
if (self.status === PENDING) {
// 只能由pedning状态 => fulfilled状态 (避免调用屡次resolve reject)
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value));
}
});
}
function reject(reason) { // reason为失败态时接收的缘由
setTimeout(() => {
// 调用reject 回调对应onRejected函数
if (self.status === PENDING) {
// 只能由pedning状态 => rejected状态 (避免调用屡次resolve reject)
self.status = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.reason));
}
});
}
// 捕获在excutor执行器中抛出的异常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
复制代码
这一部分代码咱们对resolve和reject进行了判断处理,接着咱们构造then
方法
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled状态时 执行的函数
* @param {function} onRejected rejected状态时 执行的函数
* @return {function} promise2 返回一个新的promise对象
*/
Promise.prototype.then = function (onFulfilled, onRejected) {
// 成功和失败的回调 是可选参数
// onFulfilled成功的回调 onRejected失败的回调
let self = this;
let promise2;
// 须要每次调用then时都返回一个新的promise
promise2 = new Promise((resolve, reject) => {
// 成功态
if (self.status === 'resolved') {
setTimeout(()=>{
try {
// 当执行成功回调的时候 可能会出现异常,那就用这个异常做为promise2的错误的结果
let x = onFulfilled(self.value);
//执行完当前成功回调后返回结果多是promise
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
// 失败态
if (self.status === 'rejected') {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'pending') {
// 等待态时,当一部调用resolve/reject时,将onFullfilled/onReject收集暂存到集合中
self.onResolvedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
self.onRejectedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
}
});
return promise2
}
// 其中规范要求对回调中增长setTimeout处理
复制代码
能够看到resolve和reject都有一个处理新promise的方法resolvePromise,对其进行封装,达处处理不一样状况的目的
/**
* 对resolve 进行改造加强 针对resolve中不一样值状况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2,x,resolve,reject){
if(promise2 === x){ // 若是从onFullfilled中返回的x就是promise2,就会致使循环引用报错
return reject(new TypeError('Chaining cycle'));
}
let called; // 声明避免屡次使用
// x类型判断 若是是对象或者函数
if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
// 判断是不是thenable对象
try{
let then = x.then;
if(typeof then === 'function'){
then.call(x,y=>{
if(called) return;
called = true;
resolvePromise(promise2,y,resolve,reject);
},err=>{
if(called) return;
called = true;
reject(err);
});
}else{
// 说明是普通对象/函数
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
resolve(x);
}
}
复制代码
以上基本实现了Promise的基本方法,根据Promise的用法,补充一些类上的方法
// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
Promise.prototype.catch = function(onRejected){
// 默认不写成功
return this.then(null,onRejected);
};
/**
* Promise.all Promise进行并行处理
* 参数: promise对象组成的数组做为参数
* 返回值: 返回一个Promise实例
* 当这个数组里的全部promise对象所有变为resolve状态的时候,才会resolve。
*/
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
if(++i == promises.length){
resolve(arr);
}
}
for(let i = 0;i<promises.length;i++){
promises[i].then(data=>{ // data是成功的结果
processData(i,data);
},reject);
}
})
}
/**
* Promise.race
* 参数: 接收 promise对象组成的数组做为参数
* 返回值: 返回一个Promise实例
* 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪个更快)
*/
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i = 0;i<promises.length;i++){
promises[i].then(resolve,reject);
}
})
}
复制代码
最后咱们导出方法
module.exports = Promise;
复制代码
至此一个符合PromiseA+规范的本身写的源码库完成了,能够测试使用这个库替代Promise,以测试是否有逻辑错误等,或者可使用
npm install promises-aplus-tests -g
promises-aplus-test 文件名
复制代码
插件来测试该源码是否符合PromiseA+规范
但愿这篇文章能帮到你,以上