Promise
优点:解决并发问题(同步多个异步方法的执行结果)。解决链式调用的问题(先获取a,在获取b) 解决多个回调嵌套的问题。缺点:promise无法终止,只能说是抛弃本次promise的结果。
1. A+规范
- new Promise时,需要传递一个 executor 执行器,执行器立刻执行。
- executor 接受两个参数,分别是 resolve 和 reject。
- promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled。
- promise 的状态一旦确认,就不会再改变。
- promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected。
- 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)。
- then 的参数 onFulfilled 和 onRejected 可以缺省。
- promise 可以then多次,promise 的then 方法返回一个 promise。
- 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)。
- 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)。
- 如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,就走下一个then的成功,如果失败,就走下一个then的失败。
- 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)。
- 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。
- 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值。
- 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。
2. promise测试
-
npm i promises-aplus-tests -g
-
在
promise.js
增加以下代码Promise.deferred = function(){ let dfd = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
-
promises-aplus-tests promise.js
3. catch
捕获失败的peomise。
4. Promise.finally
无论如何都执行,finally会传递前一个promise的结果但不会接管该结果,如果finally返回的是promise会等待这个promise执行完成。
5. Promise.try
能捕获同步或者异步异常。需要自己实现。
6. Promise.resolve
返回成功的promise。
7. Promise.reject
返回失败的pomise。
8. Promise.all
返回promise。全部完成才算完成,如果有一个失败,就失败。Promise.all是按照顺序执行的。
9. Promise.race
有一个成功就成功,有一个失败就失败。
10. Promise.stop
终止一个promise。
11. PromiseA+实现
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError());
}
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
let isCalled = false;
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (isCalled) return;
isCalled = true;
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (isCalled) return;
isCalled = true;
reject(r);
})
} else {
resolve(x);
}
} catch (e) {
if (isCalled) return;
isCalled = true;
reject(e)
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.status === PENDING) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallbacks.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(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected == 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
static all(promises) {
return new Promise((resolve, reject) => {
let values = [], index = 0, len = promises.length;
promises.forEach((promise, i) => {
Promise.resolve(promise).then((v) => {
values[i] = v;
++index === len ? resolve(values) : null;
}, reject)
})
})
}
static resolve(value) {
return new Promise((resolve) => {
resolve(value);
})
}
static reject(value) {
return new Promise((resolve, reject) => {
reject(value);
})
}
finally(fn) {
return this.then(v => Promise.resolve(fn()).then(() => v), e => Promise.resolve(fn()).then(() => { throw e }));
}
static stop(promise) {
let fail;
let promise2 = new Promise((resolve, reject) => {
fail = reject;
})
let race = Promise.race([promise2, promise]);
race.reject = fail;
return race;
}
static try(fn) {
return new Promise((resolve, reject) => {
resolve(fn())
})
}
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach(promise => {
Promise.resolve(promise).then(resolve, reject);
})
})
}
}
Promise.deferred = function () {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;