概述
Promise最早由社区提出并实现,典型的一些库有Q,when, bluebird等;它们的出现是为了更好地解决JavaScript中异步编程的问题,传统的异步编程最大的特点就是地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Promise则可以让我们通过链式调用的方法去解决回调嵌套的问题,使我们的代码更容易理解和维护,而且Promise还增加了许多有用的特性,让我们处理异步编程得心应手
Promise创建
Promise本意是承诺,它有三个状态(等待态、成功态、失败态), 默认是等待态。new Promise 时传递一个执行器 (executor),且执行器立即执行
//Promise是一个类console.log(2)new Promise(function ((resolve, reject) => { console.log(1)))console.log(3);输出结果如下:213复制代码
Promise.then
每个promise实例都一个then方法,then方法有两个参数(成功和失败),成功会有成功的值,失败会有失败的原因
let promise = new Promise((resolve, reject) => { //resolve('hello'); reject('hello')});promise.then(data => { console.log('data', data);}, err=>{ console.log('err', err)})输出结果如下://data helloerr hello复制代码
且同一个promise可以then多次
let promise = new Promise((resolve, reject) => { //resolve('hello'); reject('hello')});promise.then(data => { console.log('data', data);}, err=>{ console.log('err', err)});promise.then(data => { console.log('data', data);}, err=>{ console.log('err', err)})输出结果如下://data hello//data helloerr helloerr hello复制代码
基本的promise实现
Promise/A+ 规范的内容比较多,详情查看,实现 Promise 逻辑时会根据实现的部分介绍相关的Promise/A+规范内容。
class Promise { constructor(executor) { //默认的状态 this.status = 'pending'; //成功的值 this.value = undefined; //失败的原因 this.reason = undefined; //成功存放的数组 this.onResolvedCallbacks = []; //失败存放的数组 this.onRejectedCallbacks = []; //默认让执行器执行 let resolve = (value) => { if (this.status === 'pending') { this.status = 'fulfilled'; //成功 this.value = value; this.onResolvedCallbacks.forEach(fn => fn()); } } let reject = (reason) => { if (this.status === 'pending') { this.status = 'rejected'; //失败 this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } } try{ executor(resolve, reject); }catch(e) { reject(e); } } then(onFufilled, onRejected) { if (this.status === 'fulfilled') { onFufilled(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } if (this.status === 'pending') { this.onResolvedCallbacks.push(() => { onFufilled(this.value); }); this.onRejectedCallbacks.push(() => { onRejected(this.reason); }) } }}module.exports = Promise;复制代码
验证实现的promise
example 1let Promise = require('./Promise.js')let promise = new Promise((resolve, reject) => { resolve('hello')});promise.then(data => { console.log('data', data);}, err=>{ console.log('err', err)});输出结果如下data helloexample 2let Promise = require('./Promise.js')let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('hello') })});promise.then(data => { console.log('data', data);}, err=>{ console.log('err', err)});输出结果如下data hello复制代码
Promise链式调用
- Promise内部抛出异常,直接走失败执行函数
let fs = require('fs');function read (path, enconding) { return new Promise((resolve, reject) => { throw new Error('内部抛出异常'); fs.readFile(path, enconding, (err, data) => { if (err) reject(err); resolve(data); }) })}read('./3.txt', 'utf8').then(data => { console.log(data);}, err => { console.log("error", err)})输出结果如下:error Error: 内部抛出异常复制代码
- Promise解决多个异步回调(有关联)
//成功的回调,或者失败的回调执行后返回一个promise//会将这个promise的结果传给下一个then中//如果返回普通值,会将这个普通值传递到下一次then的成功参数read('./1.txt', 'utf8').then(data => { console.log('then1', data) return read(data, 'utf8');}, err => { console.log("err1", err)}).then(data => { console.log('then2', data);}, err => { console.log("err2", err)});注: 1.txt 文本内容为 "2.txt" 2.txt 文本内容为 "数据"输出结果如下:then1 2.txtthen2 数据 read('./1.txt', 'utf8').then(data => { console.log('then1', data) return read(data, 'utf8');}, err => { console.log("err1", err)}).then(data => { console.log('then2', data); return data}, err => { console.log("err2", err)}).then(data => { console.log('then3', data)}).then(data => { console.log('then4', data)});输出结果如下:then1 2.txtthen2 数据 then3 数据 then4 undefinedread('./1.txt', 'utf8').then(data => { console.log('then1', data) return read(data, 'utf8');}, err => { console.log("err1", err)}).then(data => { console.log('then2', data); return data}, err => { console.log("err2", err)}).then(data => { console.log('then3', data)}).then(data => { console.log('then4', data); throw new Error('xxxxxxxxxxxxxxxx');}).then(null, err => { console.log("err5", err)}).then(data => { console.log('then5 成功', data)}, err=> { console.log("err6 失败")})输出结果如下then1 2.txtthen2 数据 then3 数据 then4 undefinederr5 Error: xxxxxxxxxxxxxxxxthen5 成功 undefinedread('./1.txt', 'utf8').then(data => { console.log('then1', data) return read(data, 'utf8');}, err => { console.log("err1", err)}).then(data => { console.log('then2', data); return data}, err => { console.log("err2", err)}).then(data => { console.log('then3', data)}).then(data => { console.log('then4', data); throw new Error('xxxxxxxxxxxxxxxx');}).then().then(data => { console.log('then5 成功', data)}).catch(err => { console.log("catch", err)})输出结果如下then1 2.txtthen2 数据 then3 数据 then4 undefinedcatch Error: xxxxxxxxxxxxxxxx复制代码
- Promise解决多个异步回调(无关联)
Promise.all 多个promise都成功后,执行成功回调,如果有一个失败,走失败回调
let fs = require('fs');function read(path, enconding) { return new Promise((resolve, reject) => { fs.readFile(path, enconding, (err, data) => { if (err) reject(err); resolve(data); }) })}let pRead1 = read('./1.txt', 'utf-8');let pRead2 = read('./2.txt', 'utf-8');Promise.all([pRead1, pRead2]).then(res => { console.log(res);})输出结果如下[ '2.txt', '数据 ' ]复制代码
Promise.race,多个promise执行,谁先执行完,就用先执行的结果
let fs = require('fs');function read(path, enconding) { return new Promise((resolve, reject) => { fs.readFile(path, enconding, (err, data) => { if (err) reject(err); resolve(data); }) })}let pRead1 = read('./1.txt', 'utf-8');let pRead2 = read('./2.txt', 'utf-8');Promise.race([pRead2, pRead1]).then(res => { console.log(res);}, err => { console.log('err', err)})输出结果如下2.txt复制代码
- Promise.resolve、Promise.reject
Promise.resolve(123).then(data => { console.log(data)});Promise.reject('err 123').then(null, err => { console.log(err)})复制代码
实现Promise链式调用(promise实现链式调用返回一个新的promise)
class Promise { constructor(executor) {...} then(onFufilled, onRejected) { onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err;}; let promise2; promise2 = new Promise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { try{ let x = onFufilled(this.value); resolvePromise(promise2, x, resolve, reject); }catch(e) { reject(e) } }, 0); } if (this.status === 'rejected') { setTimeout(() => { try{ let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }catch(e) { reject(e) } }, 0); } if (this.status === 'pending') { this.onResolvedCallbacks.push(() => { setTimeout(() => { try{ let x = onFufilled(this.value); resolvePromise(promise2, x, resolve, reject); }catch(e) { reject(e) } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try{ let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }catch(e) { reject(e) } }, 0); }) } }); return promise2; //将每个 Promise 实例调用 then 后返回的新 Promise 实例称为 promise2,将 then 回调返回的值称为 x; }}function resolvePromise(promise2, x, resolve, reject) { /* 如果 promise2 和 x 为同一个对象,由于 x 要将执行成功或失败的结果传递 promise2 的 then 方法回调的参数, 因为是同一个 Promise 实例,此时既不能成功也不能失败(自己不能等待自己完成),造成循环引用, 这种情况下规定应该抛出一个类型错误来回绝; */ if (promise2 === x) { return reject(new TypeError('循环引用')) } let called; //如果 x 是一个对象或者函数且不是 null if (x != null && (typeof x === 'object' || typeof x === 'function')) { /* 如果 x 是对象,防止 x 是通过 Object.defineProperty 添加 then 属性,并添加 get 和 set 监听, 如果在监听中抛出异常,需要被捕获到 */ try{ let then = x.then //如果是对象,试着取下then if (typeof then === 'function') { //x.then 是一个函数,就当作 x 是一个 Promise 实例, then.call(x, y => { if (called) return; called = true; //resolve的结果依旧是promise,继续解析 resolvePromise(promise2, y, resolve, reject); }, r => { if (called) return; called = true; reject(y); }) }else { //如果 x.then 不是函数,则说明 x 为普通值,直接调用 promise2 的 resolve 方法将 x 传入, resolve(x); } }catch(e) { //取then出错,不在继续执行 if (called) return; called = true; reject(e); } }else { //不满足条件说明该返回值就是一个普通值,直接执行 promise2 的 resolve 并将 x 作为参数传入; resolve(x); }}module.exports = Promise;复制代码
验证promise链式调用
let Promise = require('./Promise')let promise = new Promise((resolve, reject) => { resolve('hello');})promise.then(data => { console.log('then1', data); return 4}).then(data => { console.log("data", data)}).then(() => { throw new Error("error");}).then(() => { console.log("success");}, err => { console.log(err);}).then(() => { console.log("成功");}, () => { console.log("失败");});输出结果如下then1 hellodata 4Error: error成功复制代码
验证参数是否穿透
let p1 = new Promise((resolve, reject) => resolve("ok"));let p2 = p1.then().then(data => { console.log(data); throw new Error("出错了");});p2.then().then(null, err => console.log(err));输出结果如下okError: 出错了复制代码
实现catch方法
class Promise { constructor(executor) {...} then(onFufilled, onRejected) {...} catch(onRejected) { return this.then(null, onRejected) }}复制代码
实现Promise.resolve方法
Promise.resolve = function (value) { return new Promise((resolve, reject) => { resolve(value); })}复制代码
实现Promise.reject方法
Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason); })}复制代码
实现Promise.all方法
Promise.all = function (promises) { return new Promise((resolve, reject) => { let result = []; let idx = 0; let processData = (index, data) => { idx++; result[index] = data; if (promises.length === idx) { resolove(result) } } for (let i = 0; i < promises.length; i++) { promises[i].then(data => { processData(i, data); }, reject) } })}复制代码
实现Promise.race方法
Promise.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(resolve, reject) } })}复制代码
测试Promise的实现代码(测试包 promises-aplus-tests)
- 全局安装 npm install promises-aplus-tests -g
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd;}//https://www.pandashen.com/2018/06/13/20180613193626/#more <-- 更多描述请查看复制代码