1、promise
使用promise(MDN)
promise(MDN)
Promises/A+标准
promisejs.org
事件循环
基本输出题
1、new Promise 的参数函数是同步执行
const p = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
p.then(() => {
console.log(3)
})
console.log(4)
// 1-> 2 -> 4 ->3
首先new Promise 的参数函数是同步的,所以先输出 1->2;
resolve 后还需要等待进入下一个事件循环。then 把参数函数推入微任务队列,并不直接执行。
输出 4,接着事件循环进入下一轮,输出 3.
2、promise的then函数不同返回值的区别
//给出一个promise
var p = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 3000)
})
//下面三种有什么区别?
// 1
p.then(() => {
return Promise.resolve(2);
}).then((n) => {
console.log(n)
});
// 2
p.then(() => {
return 2
}).then((n) => {
console.log(n)
});
// 3
p.then(2).then((n) => {
console.log(n)
});
1、输出2。Promise.resolve 就是一个 Promise 对象就相当于返回了一个新的 Promise 对象。然后在下一个事件循环里才会去执行 then
2、输出2。和上一点不一样的是,它不用等下一个事件循环。
3、输出1。then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。
3、async await
let a;
const b = new Promise((resolve, reject) => {
console.log('p1');
resolve();
}).then(() => {
console.log('p2');
}).then(() => {
console.log('p3');
}).then(() => {
console.log('p4');
});
a = new Promise(async (resolve, reject) => {
console.log(a);
await b;
console.log(a);
console.log('after1');
await a
resolve(true);
console.log('after2');
});
console.log('end');
//p1 -> undefined -> end -> p2 -> p3 -> p4 -> p{pending} -> after1
第一个输出 p1,是因为 Promise 里的方法立即执行。接着调用 resolve,只不过 then 里的方法等下一个周期
第二个输出 undefined,是因为立即执行执行 a 内部的方法,先 console.log(a),但此时的 a 还没赋值给左边的变量,所以只能是 undefined。然后 await b 就得等下一个周期执行了。
第三个输出 end,自然不意外。
接着输出 p2,p3,p4,是因为 await b 等待他执行完了,才轮到 a 内部继续执行。
输出 Promise { pending },事件都进入了循环,a 肯定已经被赋值成了 Promise 对象。所以第二遍 console.log(a),自然就输出这个了。
输出 after1 不奇怪。
await a 时,a 是必须等待 Promise 的状态从 pending 到 fullfilled 才会继续往下执行,可 a 的状态是一直得不到更改的,所以无法执行下面的逻辑。只要在 await a 上面加一行 resolve() 就能让后面的 after 2 得到输出
4、Promise 对象的状态只能被转移一次
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise
.then((res) => {
console.log('then: ', res);
})
.catch((err) => {
//永远到不这里
console.log('catch: ', err);
});
Promise 对象的状态只能被转移一次,resolve(‘success1’) 时状态转移到了 fullfilled 。后面 reject 就调用无效了,因为状态已经不是 pending。
5、promise链路中then函数抛出Error对象,也是正常走 then 的链式调用下去
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
没有抛出错误和异常,只是 return 了一个对象,即使这个对象是 Error 对象,那自然也是正常走 then 的链式调用下去了,不会触发 catch。
6、promise链路中对错误进行了捕获,后续的函数可能会继续执行
Promise.resolve()
.then(() => {
console.log('[onFulfilled_1]');
throw 'throw on onFulfilled_1';
})
.then(() => {
console.log('[onFulfilled_2]');
}, err => { // 捕获错误
console.log('[onRejected_2]', err);
})
.then(() => { // 该函数将被调用
console.log('[onFulfilled_3]');
})
.catch(err => {
console.log('[catch]', err);
});
//[onFulfilled_1] -> [onRejected_2] throw on onFulfilled_1 -> [onFulfilled_3]
7、事件循环队列的执行顺序
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
// script start -> async1 start -> async2 -> promise1 -> script end ->
// async1 end -> promise2 -> settimeout
1、执行同步代码,输出 script start;
2、遇到setTimeout,产生一个宏任务
3、然后执行async1函数,async函数还是基于Promise的一些封装,而Promise是属于微任务的一种;因此会把await async2()及后面的所有代码注册微任务。
所以先执行同步代码,输出 async1 start,然后输出 async2
4、继续执行主线程代码同步代码,输出promise1,然后输出 script end;
5、执行主线程微任务,执行第一个微任务,输出 async end;执行第二个微任务,输出promise2;
6、setTimeout属于宏任务,要等待script这个宏任务执行完再执行,所以最后输出script end
参考链接:https://cloud.tencent.com/developer/article/1601176
概念题
1、了解 Promise 吗?
2、Promise 解决的痛点是什么?
1)回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象,是为解决异步操作函数里的嵌套回调(callback hell)问题,代码臃肿,可读性差,只能在回调里处理异常
2)promise可以支持多个并发的请求,获取并发请求中的数据
3)promise可以解决可读性的问题,异步的嵌套带来的可读性的问题,它是由异步的运行机制引起的,这样的代码读起来会非常吃力
4)promise可以解决信任问题,对于回调过早、回调过晚或没有调用和回调次数太少或太多,由于promise只能决议一次,决议值只能有一个,决议之后无法改变,任何then中的回调也只会被调用一次,所以这就保证了Promise可以解决信任问题
3、Promise 解决的痛点还有其他方法可以解决吗?如果有,请列举。
1)Promise 解决的痛点还有其他方法可以解决,比如setTimeout、事件监听、回调函数、Generator函数,async/await
2)setTimeout:缺点不精确,只是确保在一定时间后加入到任务队列,并不保证立马执行。只有执行引擎栈中的代码执行完毕,主线程才会去读取任务队列
3)事件监听:任务的执行不取决于代码的顺序,而取决于某个事件是否发生
4)Generator函数虽然将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。即如何实现自动化的流程管理
5)async/await
4、Promise 如何使用?
1)创造一个Promise实例
2)Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
3)可用Promise的try和catch方法预防异常
5、Promise 常用的方法有哪些?它们的作用是什么?
1)Promise.resolve(value)
类方法,该方法返回一个以 value 值解析后的 Promise 对象
如果这个值是个 thenable(即带有 then 方法),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled)
如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回
其他情况以该值为成功状态返回一个 Promise 对象
2)Promise.reject
类方法,且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected
3)Promise.prototype.then
实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收
4)Promise.prototype.catch
实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息
5)Promise.race
类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败
6)Promise.all
类方法,多个 Promise 任务同时执行
如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果
6、Promise 在事件循环中的执行过程是怎样的?
1)事件循环
从代码执行顺序的角度来看,程序最开始是按代码顺序执行代码的,遇到同步任务,立刻执行;遇到异步任务,则只是调用异步函数发起异步请求。此时,异步任务开始执行异步操作,执行完成后到消息队列中排队。程序按照代码顺序执行完毕后,查询消息队列中是否有等待的消息。如果有,则按照次序从消息队列中把消息放到执行栈中执行。执行完毕后,再从消息队列中获取消息,再执行,不断重复。由于主线程不断的重复获得消息、执行消息、再取消息、再执行
2)promise的事件循环
Promise在初始化时,传入的函数是同步执行的,然后注册 then 回调。注册完之后,继续往下执行同步代码,在这之前,then 中回调不会执行。同步代码块执行完毕后,才会在事件循环中检测是否有可用的 promise 回调,如果有,那么执行,如果没有,继续下一个事件循环
7、Promise 的业界实现都有哪些?
1) promise可以支持多个并发的请求,获取并发请求中的数据
2)promise可以解决可读性的问题,异步的嵌套带来的可读性的问题,它是由异步的运行机制引起的,这样的代码读起来会非常吃力
8、 Promise的问题?解决办法?
promise的问题为:
promise一旦执行,无法中途取消
promise的错误无法在外部被捕捉到,只能在内部进行预判处理
promise的内如何执行,监测起来很难
解决办法
正是因为这些原因,ES7引入了更加灵活多变的async,await来处理异步
怎么取消promise [待完善…]
Promise的then方法接收两个参数:
Promise.prototype.then(onFulfilled, onRejected)
若onFulfilled或onRejected是一个函数,当函数返回一个新Promise对象时,原Promise对象的状态将跟新对象保持一致,详见Promises/A+标准。
因此,当新对象保持“pending”状态时,原Promise链将会中止执行。
Promise.resolve().then(() => {
console.log('1')
return new Promise(()=>{}) // 返回“pending”状态的Promise对象
}).then(() => {
// 后续的函数不会被调用
console.log('2')
}).catch(err => {
console.log('err', err)
})
顺序输出
给你若干个 promise 对象,你怎么保证它是顺序执行的?
var makePromise = function(value, time) {
return new Promise(function(resolve, reject){
setTimeout(function() {
resolve(value);
}, time)
})
};
function order(promises) {
}
order([
makePromise('a', 3000),
makePromise('b', 5000),
makePromise('c', 2000)
]);
1、aysnc await
async function order(promises) {
try {
for (let i = 0; i < promises.length; i++) {
let res = await promises[i]
console.log(res);
}
} catch (error) {
console.log(error);
}
}
2、不使用aysnc await的方法
function order(promises) {
let p = Promise.resolve();
for (let i = 0; i <= promises.length; i++) {
p = p.then(res => {
console.log(res);
return promises[i];
});
}
}
并发做异步请求,限制频率
举个例子,有 8 张图片 url,你需要并发去获取它,并且任何时刻同时请求的数量不超过 3 个。也就是说第 4 张图片一定是等前面那一批有一个请求完毕了才能开始,以此类推。
var urls = [
'https://www.kkkk1000.com/images/1.jpg',
'https://www.kkkk1000.com/images/2.jpg',
'https://www.kkkk1000.com/images/3.jpg',
'https://www.kkkk1000.com/images/4.jpg',
'https://www.kkkk1000.com/images/5.jpg',
'https://www.kkkk1000.com/images/6.jpg',
'https://www.kkkk1000.com/images/7.jpg',
'https://www.kkkk1000.com/images/8.jpg'
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
console.log('一张图片加载完成', url);
resolve();
};
img.onerror = reject;
img.src = url;
});
}
function limitload(urls, limit) {
}
limitload(urls, 2);
解决方案:大概思路就是先将前 limit 个同时进行,然后再通过 then 去控制,每次完成一个后再启动一个
function limitload(urls, limit) {
const execNewPromise = () => {
limit++;
if (limit < urls.length) {
loadImg(urls[limit]).then(() => execNewPromise());
}
}
let promise = Promise.resolve();
promise.then(() => {
for (let i = 0; i < limit; i++) {
loadImg(urls[i]).then(() => execNewPromise());
}
});
}