函数的执行分为同步和异步两种。
同步即为 同步连续执行,通俗点讲就是做完一件事,再去做另一件事。
异步即为 先做一件事,中间可以去做其他事情,稍后再回来做第一件事情。
三个红绿黄三个颜色的灯,然后每个灯之间会进行切换,比如绿灯1s亮然后黄灯亮1s,最后是红灯1s,一个流程过后则是进行一个循环,重新到绿灯亮。
所有异步编程方案的根基
有调用者定义,交给执行者的函数。将函数作为参数传递
// 实现亮灯的基本方法方法
const lamp = (color, callback) => {
// 打印灯的颜色
console.log(`灯颜色: ${color}`);
// 使用定时器,延时一秒
setTimeout(() => {
// 触发回调函数
callback();
}, 1000);
};
函数依次交替执行
lamp("red", () => {
lamp("yellow", () => {
lamp("green", () => {
console.log("执行完成");
});
});
});
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成
通过递归使函数循环依次交替执行
const run = () =>{
lamp("red", () => {
lamp("yellow", () => {
lamp("green", () => {
run();
});
});
});
}
run()
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成
CommonJs 为异步编程提供统一、更合理、更强大的解决方案
// 实现亮灯的基本方法方法
const lamp = (color) => {
// 返回一个promise对象
return new Promise((resolve, reject) => {
// 打印灯的颜色
console.log(`灯颜色: ${color}`);
// 使用定时器,延时一秒
setTimeout(() => {
// 触发成功承诺
resolve();
}, 1000);
});
};
函数依次交替执行
// 类似callback方式实现(未解决了回调地狱的问题)
lamp("red").then(() => {
lamp("yellow").then(() => {
lamp("green").then(() => {
console.log("执行完成");
});
});
});
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成
// 链式调用方式实现 (解决了回调地狱的问题,语义相对清晰)
// Promise 对象的then方法会返回一个全新的Promise对象
// 后面的then方法就是在为上一个then返回的Promise注册回调
// 前面的then方法中回调函数的返回值会作为后面then方法的回调参数
// 如果回调中返回的是Promise,那后面的then方法的回调就会等待它的结束
lamp("red")
.then(() => {
return lamp("yellow");
})
.then(() => {
return lamp("green");
})
.then(() => {
console.log("执行完成");
});
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成
通过递归使函数循环依次交替执行
const run = () => {
lamp("red")
.then(() => {
return lamp("yellow");
})
.then(() => {
return lamp("green");
})
.then(() => {
run();
});
};
run()
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成
// 实现亮灯的基本方法方法
const lamp = (color) => {
// 返回一个promise对象
return new Promise((resolve, reject) => {
// 打印灯的颜色
console.log(`灯颜色: ${color}`);
// 使用定时器,延时一秒
setTimeout(() => {
// 触发成功承诺
resolve();
}, 1000);
});
};
函数依次交替执行
function* run() {
yield lamp("red");
yield lamp("green");
yield lamp("yellow");
}
const co = run();
co.next()
.value.then(() => {
return co.next().value;
})
.then(() => {
return co.next().value;
})
.then(() => {
return co.next().value;
});
通过递归使函数循环依次交替执行
function* run() {
yield lamp("red");
yield lamp("green");
yield lamp("yellow");
}
const run2 = () => {
const co = run();
co.next()
.value.then(() => {
return co.next().value;
})
.then(() => {
return co.next().value;
})
.then(() => {
return co.next().value;
})
.then(() => {
run2();
});
};
run2();
// 实现亮灯的基本方法方法
const lamp = (color) => {
// 返回一个promise对象
return new Promise((resolve, reject) => {
// 打印灯的颜色
console.log(`灯颜色: ${color}`);
// 使用定时器,延时一秒
setTimeout(() => {
// 触发成功承诺
resolve();
}, 1000);
});
};
// 执行方法
const run = async () => {
await lamp("red");
await lamp("green");
await lamp("yellow");
run();
};
// 执行
run()
需求在写小程序的时候有很多wx.xxx 方法不支持 promise 模式调用,但是支持 success 与 fail 的callback回调方式。所以如何有效的封装一个函数,优雅的调用 wx.xxx
const promiseFn = function (func, params = {}) {
// 返回promise对象
return new Promise((resolve, reject) => {
// 传入参数混入 success 与 fail
const args = Object.assign(params, {
success: (res) => {
// 成功承诺
resolve(res);
},
fail: (error) => {
// 失败承诺
reject(error);
},
});
// 调用传入的函数
func(args);
});
};
// 模拟测试函数
const wx = (options) => {
setTimeout(() => {
options.success && options.success(options.key);
}, 100);
};
// 通过promiseFn 调用 wx 函数,并且传递对象为参数
promiseFn(wx, { key: 'hello' })
.then((res) => {
console.log(res);
});
// 控制台打印
// hello
进入一个页面或者一个组件,经常会向后端发送2个或更多的请求。如何使用promise优雅的处理呢
// 模拟ajax请求发送
const ajax = (data) =>
new Promise((resolve, _reject) => {
setTimeout(() => {
console.log(`ajax: ${data.key}`);
resolve(data);
// 因为使用了随机数延时,所以请求先后可能不一致
}, Math.round(Math.random() * 1000));
});
const loadData = async () => {
// showLoading()
// 如果需要等待三个请求完毕
const [ res1,res2,res3 ] = await Promise.all([
ajax({ key: "请求1" }),
ajax({ key: "请求2" }),
ajax({ key: "请求3" }),
]);
console.log(res1,res2,res3)
// hiddenLoading()
};
loadData();
请求1``请求2``请求3
同时提交,请求4
依赖于请求2的结果
// 模拟ajax请求发送
const ajax = (data) =>
new Promise((resolve, _reject) => {
setTimeout(() => {
console.log(`ajax: ${data.key}`);
resolve(data);
}, Math.round(Math.random() * 1000));
});
// 组合请求2 结束后 执行请求4
const ajax2And4 = async () => {
const res = await ajax({ key: "请求2" });
const res2 = await ajax({ key: res.key + "_请求4" });
return res2;
};
// 加载方法
const loadData = async () => {
// showLoading()
const [res1, res2, res3] = await Promise.all([
ajax({ key: "请求1" }),
// 调用组合封装的函数
ajax2And4(),
ajax({ key: "请求3" }),
]);
console.log(res1, res2, res3);
// hiddenLoading()
};
loadData();
链式调用实现方式,当程序较为复杂时 此种写法逻辑不是很清晰且自由度不高
只是将实现思路,因为目前的请求类库都已支持超时设置,所以不要在生产代码中使用
// 模拟ajax请求发送
const ajax = (data) =>
new Promise((resolve, _reject) => {
setTimeout(() => {
console.log(`ajax: ${data.key}`);
resolve(data);
}, Math.round(Math.random() * 1000));
});
// 延时函数
const timerOutFn = (timeout) =>
new Promise((_resolve, reject) => {
setTimeout(() => reject(new Error("timeout")), timeout);
});
// 使用新函数包裹 ajax 与延时函数
const ajaxAndTimer = async (params, { timeout = 500 }) => {
return await Promise.race([ajax(params), timerOutFn(timeout)]);
};
// 使用
ajaxAndTimer({ key: "请求1" }, { timeout: 200 });
// 控制台打印
// 未超时
// ajax: 请求1
// 超时
// UnhandledPromiseRejectionWarning: Error: timeout