关于本文
Tips: 本文参考标准规范为 Promises/A+
[链接:https://promisesaplus.com/]
Promise 来源
起源:Promise最初被提出是在 E语言中, 它是
基于并列/并行处理
设计的一种编程语言。核心:解决
回调地狱
的问题-
原JS异步处理方式,存在如下问题:
非Promise实现异步处理方式,如下:
let fs = require('fs'); fs.readFile('./2.promise/1.txt','utf8',function(err,data){ fs.readFile(data,'utf8',function(err,data){ console.log(data); }); });
总结存在问题(虽然都能解决,但是可拓展性太差):
try/catch问题
- 无法在同一时刻
合并两次异步的结果
,异步方案不支持return
-
Promise的处理方式
- 理念:Promise是把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。
var promise = getAsyncPromise("fileA.txt"); promise.then(function(result){ // 获取文件内容成功时的处理 ... }).catch(function(error){ // 获取文件内容失败时的处理 ... });
Promise 状态流
用new Promise 实例化的promise对象有以下三个状态。
Pending
(es6, has-resolution)
,
既不是resolve也不是reject的状态。 promise对象刚被创建后的初始化状态
等resolved
(es6, has-rejection)
,
resolve(成功)时,此时会调用 onFulfilled
rejected
(es6,unresolved)
,
reject(失败)时。此时会调用 onRejected
Promise API
三个类型,内含部分源码实现
- Constructor
- 状态机
status
- 处理函数
reject, resolve
- 执行函数
executor
- 状态机
- Instance Method
-
Resolve callback
(onFulfilled会被调用)
function resolve(value) { // 成功状态 if (self.status === 'pending') { self.status = 'resolved'; self.value = value; // 执行成功的回调 self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } }
-
Reject callback
(onRejected 会被调用)
function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; // 执行是失败的回调 self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } }
- promise.then 成功和失败时都可以使用。 另外在只想对异常进行处理时可以采用
promise.then(undefined, onRejected)
这种方式,只指定reject时的回调函数即可。 - 不过这种情况下
promise.catch(onRejected)
应该是个更好的选择。
- promise.then 成功和失败时都可以使用。 另外在只想对异常进行处理时可以采用
-
Static Method
(静态方法)
-
Promise.all
原理:hold住所有的队列中的实例,等待所有实例执行完毕后,执行resolve 一起返回
Promise.all = function(promises) { //promises是一个promise的数组 return new Promise(function (resolve, reject) { //arr是最终返回值的结果 let arr = []; // 表示成功了多少次 let i = 0; function processData(index, y) { arr[index] = y; if (++i === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; i++) { promises[i].then(function (y) { processData(i, y) }, reject) } }); }
-
Promise.catch
原理:利用的是promise实例的链式调用中,如果return 一个error flag(例如: null, throw new Error等),则直接走到 Reject callback 中
// 捕获错误的方法 Promise.prototype.catch = function (callback) { return this.then(null, callback) }
-
Promise.race
释义:只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) { return new Promise(function (resolve, reject) { for (var i = 0; i < promises.length; i++) { promises[i].then(resolve,reject) } }) }
-
Promise.resolve
释义:生成一个成功的promise
Promise.resolve = function(value){ return new Promise(function(resolve,reject){ resolve(value); }) }
-
Promise.reject
释义:生成一个失败的promise
Promise.reject = function(reason){ return new Promise(function(resolve,reject){ reject(reason); }) }
-
-
Promise 核心方法实现
- 核心特性
-
多次then的实现
- 原理:promise实例可以多次then,当成功后会将then中的成功方法按顺序执行,我们可以先
将then中的成功的回调和失败的回调存到数组内,当成功时调用成功的数组即可
- 原理:promise实例可以多次then,当成功后会将then中的成功方法按顺序执行,我们可以先
-
链式调用
- 原理:promise实现链式调用靠的是
返回一个新的promise
- 原理:promise实现链式调用靠的是
-
兼容所有返回结果
-
有值就进入下一个的then中
如果then中无论是成功的回调还是失败的回调只要返回了结果就会走下一个then中的成功,如果有错误走下一个then的失败
-
普通值
,例如string类型
如果第一个promise返回一个普通值,
会进到下一次then的成功的回调
-
Error 值
,例如throw new Error()
如果第一个promise返回一个Error值,
会进到下一次then的失败的回调
-
promise
如果第一个promise返回了一个promise,需要
等待返回的promise执行后的结果传递给下一次then中
-
-
resolvePromise 处理重复返回promise的方法
-
校验循环引用
resolvePromise 返回的结果和promise是同一个那么永远不会成功和失败,导致循环引用
// 预防循环引用 if (promise2 === x) { //这里应该报一个类型错误,有问题 return reject(new TypeError('循环引用')) }
-
-
* `校验value是不是一个promise`
如果x是对象,并且x的then方法是函数,我们就认为他是一个promise
```
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
....
}
```
* `校验promise 是否ok`
有些人写的promise可能会既调用成功 又调用失败,如果两个都调用先调用谁另一个就忽略掉
* promise中`值的穿透`
* 原理:校验方法是不是又返回值,如果没有,则`内置回调方法,默认把值往下传递`
```
//成功和失败默认不穿给一个函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
```
* promise规范中要求,`所有的onFufiled和onRjected都需要异步执行,setTimeout`
```
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x可能是别人promise,写一个方法统一处理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
```
`如下开始展示源码实现`
-
构造函数 Promise 的实现
function Promise(executor) { // executor是一个执行函数 let self = this; self.status = 'pending'; // 状态机,默认值为pending,初始化的值 self.value = undefined; // 默认成功的值 self.reason = undefined; // 默认失败的原因 self.onResolvedCallbacks = []; // 存放then成功的回调 self.onRejectedCallbacks = []; // 存放then失败的回调 // 成功时的处理函数 function resolve(value) { // 成功状态 if (self.status === 'pending') { self.status = 'resolved'; self.value = value; self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } } // 失败时的处理函数 function reject(reason) { if (self.status === 'pending') { self.status = 'rejected'; self.reason = reason; self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } } try { executor(resolve, reject) } catch (e) { // 捕获的时候发生异常,就直接失败了 reject(e); } }
-
then方法实现
- 步骤1处理了值穿透的问题,case如下:
promise.then().then().then(function() { ... });
结构代码:
Promise.prototype.then = function (onFulfilled, onRjected) { // 步骤1: // 成功和失败默认不穿给一个函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value; } onRjected = typeof onRjected === 'function' ? onRjected : function (err) { throw err; } let self = this; let promise2; // 步骤2: // 成功的处理 if (self.status === 'resolved') { promise2 = new Promise(function (resolve, reject) { // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态 // x可能是一个promise 也有可能是一个普通的值 setTimeout(function () { try { let x = onFulfilled(self.value); // x可能是别人promise,写一个方法统一处理 resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 步骤3: // 错误的处理 if (self.status === 'rejected') { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onRjected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 步骤4: // 等待异步执行的过程当,即调用then时可能没成功 也没失败 // 此时还需要同步的回调函数push到队列中,方便后面的回调使用 if (self.status === 'pending') { promise2 = new Promise(function (resolve, reject) { // 此时没有resolve 也没有reject self.onResolvedCallbacks.push(function () { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }); self.onRejectedCallbacks.push(function () { setTimeout(function () { try { let x = onRjected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }); }) } return promise2; }
- 步骤1处理了值穿透的问题,case如下:
-
处理函数,resolvePromise方法实现
function resolvePromise(promise2, x, resolve, reject) { // 校验:循环引用 if (promise2 === x) { return reject(new TypeError('循环引用了')) } // 校验:表示是否调用过成功或者失败 let called; // 校验:看x是不是一个promise,promise应该是一个对象 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了 try { let then = x.then; if (typeof then === 'function') { // 成功 then.call(x, function (y) { if (called) return called = true; // y可能还是一个promise,在去解析直到返回的是一个普通值 resolvePromise(promise2, y, resolve, reject) }, function (err) { //失败 if (called) return called = true reject(err); }) } else { resolve(x) } } catch (e) { if (called) return called = true; reject(e); } } else { // 说明是一个普通值1 resolve(x); } }
Promise 语法糖
原理:语法糖负责帮你简化了逻辑,代码结构如下
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
类似Q.js用法如图:
let fs = require('fs');
let Q = require('q');
function readFile(url) {
let defer = Q.defer();
require('fs').readFile(url, 'utf8', function (err, data) {
if (err) defer.reject(err);
defer.resolve(data);
});
return defer.promise
}