Promise
Promise/A 内部原理与常见接口实现
try {
let arrayLike = {
0: Promise.resolve("233"),
length: 1,
};
Promise.all(arrayLike);
} catch (e) {
console.log("error");
}
const promises = [
new Promise(function (resolve) {
setTimeout(function () {
resolve(1);
}, 90);
}),
new Promise(function (resolve) {
setTimeout(function () {
resolve(2);
}, 10);
}),
new Promise(function (resolve) {
setTimeout(function () {
resolve(3);
}, 50);
}),
];
Promise.all(promises).then(console.log);
异步设计模式
在日常的项目开发中我们经常会需要处理异步调用,本部分我们即讨论如何以回调、Promise 以及 async/await 来实现常见的异步模式。
失败重试
function requestWithRetry(url, retryCount) {
if (retryCount) {
return new Promise((resolve, reject) => {
const timeout = Math.pow(2, retryCount);
setTimeout(() => {
console.log("Waiting", timeout, "ms");
_requestWithRetry(url, retryCount).then(resolve).catch(reject);
}, timeout);
});
} else {
return _requestWithRetry(url, 0);
}
}
function _requestWithRetry(url, retryCount) {
return request(url, retryCount).catch((err) => {
if (err.statusCode && err.statusCode >= 500) {
console.log("Retrying", err.message, retryCount);
return requestWithRetry(url, ++retryCount);
}
throw err;
});
}
requestWithRetry("http://localhost:3000")
.then((res) => {
console.log(res);
})
.catch((err) => {
console.error(err);
});
function wait(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}
async function requestWithRetry(url) {
const MAX_RETRIES = 10;
for (let i = 0; i <= MAX_RETRIES; i++) {
try {
return await request(url);
} catch (err) {
const timeout = Math.pow(2, i);
console.log("Waiting", timeout, "ms");
await wait(timeout);
console.log("Retrying", err.message, i);
}
}
}
数组转换
function asyncThing(value) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(value), 100);
});
}
async function mapArray() {
return [1, 2, 3, 4].map(async (value) => {
const v = await asyncThing(value);
return v * 2;
});
}
async function filterArray() {
return [1, 2, 3, 4].filter(async (value) => {
const v = await asyncThing(value);
return v % 2 === 0;
});
}
async function reduceArray() {
return [1, 2, 3, 4].reduce(async (acc, value) => {
return (await acc) + (await asyncThing(value));
}, Promise.resolve(0));
}
// [ Promise { <pending> }, Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
filterArray().then((v) => {
console.log(v);
});
// [ 1, 2, 3, 4 ]
typeof new Promise((resolve, reject) => {}) === "object"; // true
Promise 本质上只是普通的 JavaScript 对象,包含了允许你执行某些异步代码的方法。
const fetch = function (url) {
return new Promise((resolve, reject) => {
request((error, apiResponse) => {
if (error) {
reject(error);
}
resolve(apiResponse);
});
});
};
class SimplePromise {
constructor(executionFunction) {
this.promiseChain = [];
this.handleError = () => {};
this.onResolve = this.onResolve.bind(this);
this.onReject = this.onReject.bind(this);
// 这里就可以发现传入的带回调的函数时立即执行的
executionFunction(this.onResolve, this.onReject);
}
then(onResolve) {
this.promiseChain.push(onResolve);
return this;
}
catch(handleError) {
this.handleError = handleError;
return this;
}
onResolve(value) {
let storedValue = value;
try {
this.promiseChain.forEach((nextFunction) => {
storedValue = nextFunction(storedValue);
});
} catch (error) {
this.promiseChain = [];
this.onReject(error);
}
}
onReject(error) {
this.handleError(error);
}
}
当我们使用 new Promise((resolve, reject) => {/* ... */})
这样的形式去创建 Promise 对象时,传入的 executionFunction 函数的两个参数 resolve 与 reject,实际上映射到了 SimplePromise 类的 onResolve 与 onReject 方法。而构造器同样会创建内置的 promiseChain 数组,该数组用于记录通过 then 设置的异步传入值;而 handleError 则用于响应 onReject 回调。在原生的 Promise 实现中,then 与 catch 都返回的是新的 Promise 对象,在 SimplePromise 的实现中我们则忽略了这一步。另外,原生的 Promise 实现中允许添加多个 catch 回调,并且不需要跟随在 then 后面。
Promise.all
Promise.finally
connect
combineReducer
function connect(mapStateToProps, mapDispatchToProps) {
// 构造好的封装组件
class HOCComponent extends Component {
// 利用 Context 进行状态传递, Todo
getChildContext() {}
handleProps() {
let propsFromState;
if (typeof mapStateToProps === "function") {
// 计算映射之后的 Props 值
propsFromState = mapStateToProps(state);
} else {
propsFromState = mapStateToProps;
}
return { ...propsFromState, ...mapDispatchToProps };
}
render() {
// 将新的 Props 映射入组件
return React.createElement(
HOCComponent.WrappedComponent,
this.handleProps(mapStateToProps, mapDispatchToProps)
);
}
}
// 高阶函数
function wrap(WrappedComponent) {
HOCComponent.WrappedComponent = WrappedComponent;
// 返回创建好的高阶函数
return HOCComponent;
}
// 返回需要封装的高阶函数
return wrap;
}
function combineReducers(reducers) {
// 获取所有函数键
const reducerKeys = Object.keys(reducers);
// 返回封装之后的函数
return function finalReducer(state = {}, action) {
// 最终的状态
const nextState = {};
// 依次对于 Reducer 进行处理
for (const key of reducerKeys) {
// 获取 reducer
const reducer = reducers[key];
// 获取状态树中的子对象
const stateByKey = state[key];
// 执行 reduce 转换操作
const nextStateByKey = reducer(stateByKey, action);
// Redux 需要避免状态空,进行异常检测
if (typeof nextStateByKey === "undefined") {
throw new Error("Invalid Reducer");
}
// 将新的状态对象挂载
nextState[key] = nextStateByKey;
}
return nextState;
};
}
function clientCacheMiddleware({ maxAge = 3600 * 24 * 365 }) {
return function (req, res, next) {
res.setHeader("Cache-Control", `max-age=${maxAge}`);
next();
};
}
Promise 编排
/*
* promiseSerial resolves Promises sequentially.
* @example
* const urls = ['/url1', '/url2', '/url3']
* const funcs = urls.map(url => () => $.ajax(url))
*
* promiseSerial(funcs)
* .then(console.log)
* .catch(console.error)
*/
const promiseSerial = (funcs) =>
funcs.reduce(
(promise, func) =>
promise.then((result) =>
func().then(Array.prototype.concat.bind(result))
),
Promise.resolve([])
);
// some url's to resolve
const urls = ["/url1", "/url2", "/url3"];
// convert each url to a function that returns a promise
const funcs = urls.map((url) => () => $.ajax(url));
// execute Promises in serial
promiseSerial(funcs).then(console.log).catch(console.error);
Promise.race: 返回第一个确定状态
race 函数返回一个 Promise,这个 Promise 根据传入的 Promise 中的第一个确定状态 – 不管是接受还是拒绝 – 的状态而确定状态。
const p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "一");
});
const p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, "二");
});
Promise.race([p1, p2]).then(function (value) {
console.log(value); // "二"
// 两个都解决,但p2更快
});
const p3 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, "三");
});
const p4 = new Promise(function (resolve, reject) {
setTimeout(reject, 500, "四");
});
Promise.race([p3, p4]).then(
function (value) {
console.log(value); // "三"
// p3更快,所以被解决(resolve)了
},
function (reason) {
// 未被执行
}
);
const p5 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, "五");
});
const p6 = new Promise(function (resolve, reject) {
setTimeout(reject, 100, "六");
});
Promise.race([p5, p6]).then(
function (value) {
// 未被执行
},
function (reason) {
console.log(reason); // "六"
// p6更快,所以被拒绝(reject了)
}
);
function executeAsyncTask() {
return functionA().then((valueA) => {
return functionB(valueA).then((valueB) => {
return functionC(valueA, valueB);
});
});
}
const converge =
(...promises) =>
(...args) => {
let [head, ...tail] = promises;
if (tail.length) {
return head(...args).then((value) =>
converge(...tail)(...args.concat([value]))
);
} else {
return head(...args);
}
};
functionA(2).then((valueA) => converge(functionB, functionC)(valueA));
let promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('2 in setTimeout');
resolve(2);
},0);
resolve(1);
});
promise.then((value)=>{
console.log(value);
})
// 1
// 2 in setTimeout
const prom = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(5);
}, 1000);
});
prom.then((value) => {
console.log(value);
});
setTimeout(() => {
prom.then((value) => {
console.log(value);
});
}, 5000);
Promise.all: 并发执行
async/await 默认情况下是顺序执行的,
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return function3(valueA, valueB);
}
async function executeParallelAsyncTasks() {
const [valueA, valueB, valueC] = await Promise.all([
functionA(),
functionB(),
functionC(),
]);
doSomethingWith(valueA);
doSomethingElseWith(valueB);
doAnotherThingWith(valueC);
}