手写 promise
# Promise 介绍
引用阮一峰老师的话:
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise 的作用:
- 解决嵌套地狱问题;
- 解决信任问题: 除去书写的不优雅和维护的困难以外,回调函数其实还存在信任问题。
$.ajax("xxxxxx", function success(result1) {
//回调函数不一定会像你期望的那样被调用。因为控制权不在你的手上,不能抱歉此函数一定会被调用
});
# 要求实现 polyfill
function Promise(executor) {
var self = this;
self.value = undefined;
self.status = "pending";
self.onResolvedCallback = [];
self.onRejectedCallback = [];
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
//原则上,promise.then(onResolved, onRejected)里的这两相函数需要异步调用,关于这一点,标准里也有说明:https://promisesaplus.com/#point-67
setTimeout(function() {
// 异步执行所有的回调函数
if (self.status === "pending") {
self.status = "resolved";
self.data = value;
for (var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value);
}
}
});
}
function reject(reason) {
setTimeout(function() {
// 异步执行所有的回调函数
if (self.status === "pending") {
self.status = "rejected";
self.data = reason;
for (var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
});
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
/**
@params promise2 当前then返回的promise
@params x 执行了当前then的函数,返回的结果
@resolve 当前then返回的promise 的resolve
@reject 当前then返回的promise 的reject
**/
function resolvePromise(promise2, x, resolve, reject) {
var then;
var thenCalledOrThrow = false;
if (promise2 === x) {
return reject(new TypeError("Chaining cycle detected for promise!"));
}
// 执行了当前then的函数,返回的结果还是promise则需要先处理then返回的promise
if (x instanceof Promise) {
// 如果x的状态还没有确定,那么它是有可能被一个thenable决定最终状态和值的
// 所以这里需要做一下处理,而不能一概的以为它会被一个“正常”的值resolve
if (x.status === "pending") {
x.then(function(v) {
resolvePromise(promise2, v, resolve, reject);
}, reject);
} else {
// 但如果这个Promise的状态已经确定了,那么它肯定有一个“正常”的值,而不是一个thenable,所以这里直接取它的状态
x.then(resolve, reject);
}
return;
}
// 如果then执行的结果为Object/function
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
// 2.3.3.1 因为x.then有可能是一个getter,这种情况下多次读取就有可能产生副作用
// 即要判断它的类型,又要调用它,这就是两次读取
then = x.then; //because x.then could be a getter
if (typeof then === "function") {
// 如果
then.call(
x,
function rs(y) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return resolvePromise(promise2, y, resolve, reject);
},
function rj(r) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (thenCalledOrThrow) return;
thenCalledOrThrow = true;
return reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function(onResolved, onRejected) {
var self = this;
var promise2;
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onResolved =
typeof onResolved === "function"
? onResolved
: function(v) {
return v;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function(r) {
throw r;
};
if (self.status === "resolved") {
return (promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
// 异步执行onResolved
try {
var x = onResolved(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
}
if (self.status === "rejected") {
return (promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
// 异步执行onRejected
try {
var x = onRejected(self.data);
resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
});
}));
}
if (self.status === "pending") {
// 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
return (promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(value);
resolvePromise(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (r) {
reject(r);
}
});
}));
}
};
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
# 为什么 Promise.then 返回的是一个异步函数
仔细观察上面的代码,我们发在在 then 函数中return new Promise
里面使用了setTimeout
;个人理解其主要的原因是为了保证代码执行顺序的一致性;
假设有这么一段代码:
let cache = null;
function getValue() {
if (cache) {
return Promise.resolve(cache); // 存在cache,这里为同步调用
}
return fetch("/api/xxx").then((r) => (cache = r)); // 这里为异步调用
}
console.log("before getValue");
getValue.then(() => console.log("getValue"));
console.log("after getValue");
如果在 Promise.then
返回的是同步的情况,在有缓存的情况下(cache!=null
)程序调用结果如下:
before getValue
getValue
after getValue
当缓存未被 resolved 的情况下,返回 pending promise, then 为异步,打印如下:
before getValue
after getValue
getValue
当 getValue 的缓存机制作为内部实现,接口作为黑盒提供给外部调用时,调用方完全无法判断内部到底是同步还是异步,对程序的可维护性和可测性带来极大影响。如果都统一设计为异步,则永远只会有一种确定的情况,从可维护和可测性角度来看都无疑是最佳实践。
# all 和 race 和 finally 的实现
all 和 race 两个函数都是并发执行 promise 的方法,他们的返回值也是 promise,all 会等所有的 promise 都决议之后决议,而 race 是只要有一个决议就会决议。
Promise.all = function(promises) {
let results = new Array(promises.length);
let promiseCount = 0;
let promisesLength = promises.length;
return new Promise(function(resolve, reject) {
for (let index = 0; index < promises.length; index++) {
const ele = promises[index];
(function(index) {
Promise.resolve(ele).then(
function(res) {
results[index] = res;
promiseCount++;
if (promiseCount === promisesLength) {
return resolve(results);
}
},
function(err) {
return reject(err);
}
);
})(index);
}
});
};
Promise.race = function(arr) {
return new Promise(function(resolve, reject) {
if (!Array.isArray(arr)) {
return reject(new TypeError("Promise.race accepts an array"));
}
for (var i = 0, len = arr.length; i < len; i++) {
// 谁的异步时间段就处理谁
Promise.resolve(arr[i]).then(resolve, reject);
}
});
};
Promise.resolve = function(value) {
if (value && typeof value === "object" && value.constructor === Promise) {
return value;
}
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject = function(value) {
return new Promise(function(resolve, reject) {
reject(value);
});
};
Promise.prototype.finally =
Promise.prototype.finally ||
{
finally(fn) {
const onFinally = (callback) => Promise.resolve(fn()).then(callback);
return this.then(
(result) => onFinally(() => result),
(reason) => onFinally(() => Promise.reject(reason))
);
},
}.finally;
# 如何停止一个 Promise 链?
参考:从如何停掉 Promise 链说起 (opens new window)
# 用 Promise 封装一下原生 ajax
function ajaxPromise(url, method, data, async, timeout) {
var xhr = new XMLHttpRequest();
return new Promise(function(resolve, reject) {
xhr.open(method, url, async);
xhr.timeout = options.timeout;
xhr.onloadend = function() {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)
resolve(xhr);
else
reject({
errorType: "status_error",
xhr: xhr,
});
};
xhr.send(data);
//错误处理
xhr.onabort = function() {
reject(
new Error({
errorType: "abort_error",
xhr: xhr,
})
);
};
xhr.ontimeout = function() {
reject({
errorType: "timeout_error",
xhr: xhr,
});
};
xhr.onerror = function() {
reject({
errorType: "onerror",
xhr: xhr,
});
};
});
}
- 本文链接: https://mrgaogang.github.io/javascript/base/promise.html
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!
← new_A和A的区别 一些手写的小样例 →