目前来说,JS实现异步编程的最好方法还是: ①. Promise , ②. aysnc await方法
基本原则:
如果只用Promise, 那异步过程只在Promise里, 如果想执行Promise后返回结果, 那就用 aysnc/await 调用Promise
Promise 方法
简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:f1().then(f2);
aysnc await 方法
如果,一个函数用async修饰,它return出来的就会是一个promise
执行函数时, 可以用 await修饰, 用以执行异步函数
一. 一个生动的Promise例子
下面这个例子是个非常经典的例子, 忘记了从哪位老铁哪里摘抄的了, 抱歉, 但是感谢!
//这是个普通函数
function read() {
console.log('小明认真读书');
}
//这是个异步函数
function eat() {
return new Promise((resolve, reject) => {
console.log('好嘞,吃饭咯');
setTimeout(() => {
resolve('饭吃饱啦');
}, 1000)
})
}
//这是个异步函数
function wash() {
return new Promise((resolve, reject) => {
console.log('唉,又要洗碗');
setTimeout(() => {
resolve('碗洗完啦');
}, 1000)
})
}
//这是个异步函数
function mop() {
return new Promise((resolve, reject) => {
console.log('唉,还要拖地');
setTimeout(() => {
resolve('地拖完啦');
}, 1000)
})
}
//这是个异步函数 但写法和上面不太一样, 其实它是一个Promise对象
const cooking = new Promise((resolve, reject) => {
console.log('妈妈认真做饭');
setTimeout(() => {
resolve('小明快过来,开饭啦');
}, 3000);
})
//程序运行开始:
//1. 先执行这个异步函数
cooking.then(msg => {
console.log(msg);
return eat();
}).then(msg => {
console.log(msg);
return wash();
}).then(msg => {
console.log(msg);
return mop();
}).then(msg => {
console.log(msg);
console.log('终于结束啦,出去玩咯')
})
//2. 再执行这个普通函数
read();
我们来简单分析一下为什么会这么执行
- 程序一开始,我们先执行了cooking
cooking里有一句console.log('妈妈认真做饭');
是立即执行的语句,并不需要异步处理,所以会立即打印
cooking 里的定时器是需要一定的执行时间的,所以是一个异步方法, 会呆一会才能执行到,后续的.then里的函数都要等这个方法resolve或者reject里才能执行 - 但程序不会等他 resolve 而是继续执行
于是执行到了read()
所以会打印小明认真读书
- 假设此时cooking的异步方法resolve了, 就会执行.then, 从而触发eat
- eat里也是有同步,有异步, 以此类推....
二. Promise异步带循环
var tasks = [];
function output(j) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(new Date(), j);
resolve(j*2);
}, 1000 * i);
}).then(j=>{
console.log("j*2=",j);
})
}
for (var i = 0; i < 1000; i++) {
if (tasks.length < 10) {
tasks.push(output(i)); //执行顺序:首先在这里将定时器设置好,
} else {
break;
}
// 也就是循环设置定时器,由于执行时间很快,每次循环的间隔可以忽略不计,
// 所以可以认为是设置了5个时间分别为0~4s的定时器,已经开始计时。
// 计时后返回promise对象,放在tasks数组中
}
console.log(tasks);
我们打印一下tasks发现: 在程序一开始,他就有了十个Promise子元素, 也就是说for循环和最底部的console.log(tasks);
是立即执行的, 这不难理解, 因为它们是同步函数
但是为什么下面的那些打印如此规整的排列了下来呢?而且时间间隔完全对呢???
首先: 因为每个元素是按顺序push进去的,所以会按顺序执行每条的output(i)
, 其次: 每次的output(i)都是异步的,他们形成了一个异步处理队列, 只有上一个异步做完了,下一个异步才会开始
三. aysnc await方法
如果,一个函数用async修饰,它return出来的就会是一个promise
执行函数时, 可以用 await修饰, 用以执行异步函数
async function test1() { //因为里面返回的本身就是个promise,所以不用async修饰也可以
return new Promise(resolve => {
setTimeout(() => {
resolve('hola') //异步执行后,调用resolve()改变异步状态
}, 3000);
})
}
async function test2() {
const v = await test1(); //等待异步方法test1执行完成把值传给v
console.log(v);
}
test2(); //执行时,会延时3秒打印 ho'la
四. aysnc await方法实现循环异步
async function add(i) { //因为里面返回的本身就是个promise,所以不用async修饰也可以
return new Promise(resolve => {
setTimeout(() => {
resolve(++i) //异步执行后,调用resolve()改变异步状态
}, 2000);
})
}
async function test3() {
let count = 0;
while (count < 10) {
count = await add(count); //等待异步方法test1执行完成把值传给v
console.log(count);
}
}
test3()
因为add函数被 await修饰了, 所以count会等待add 的返回值, 返回后才往下进行
网友评论