1. promise概念
promise每次调用都是异步的,代表一个异步操作。有三种状态:
- pending(进行中)、
- fulfilled(已成功)
- rejected(已失败)
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
-
promise的缺点:
(1) 无法取消promise,一旦新建它会立即执行,无法中途取消
(2) 如果不设置回调函数,promise内部抛出的错误,不会反应到外部
(3) promise的状态一旦改变,就不会再变,且无法返回之前的状态
(4) 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成) -
promise的优点:
(1) 对象的状态不受外界影响。
(2) 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
(3) 将异步操作以同步操作的流程表达出来,避免了层层嵌套回调函数。
(4) 提供统一的接口,使得控制异步操作更加容易。 -
错误处理尽量在catch中执行,因为可以将错误统一处理。强烈建议在所有队列最后都加上 .catch(),以免漏掉错误处理造成意想不到的问题
-
promise.then(onFulfilled, onRejected),resolve(成功)时
onFulfilled
会被调用,reject(失败)时onRejected
会被调用 -
Promise.all
在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是Promise.race
只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。 -
使用
promise.then(onFulfilled, onRejected)
的话在onFulfilled
中发生异常的话,在onRejected
中是捕获不到这个异常的。因此建议,不要将错误处理写在then中的第二个函数内,而是在结尾写在.catah()内进行统一错误处理
2. promise的使用 由浅到深
- 最基础用法
let promise = new Promise((resolve, reject) => {
resolve("this is information");
reject("这是错误消息");
});
promise.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
- 也可以这样使用
let promise = getAsyncPromise("fileA.txt");
promise.then(function(result) {
// 获取文件内容成功时的处理
}).catch(function(error) {
// 获取文件内容失败时的处理
});
- 这种使用方式也比较普遍。在then方法里,写两个函数也能实现then和catch的作用。第二个函数代表catch
function asyncFunction() {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve("Async Hello World!");
}, 2000);
});
}
asyncFunction()
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
asyncFunction().then(
function (value) {
console.log(value);
},
function (error) {
console.log(error);
}
);
3. 将get请求封装成一个promise
-
注意状态码这块,不能直接判断200 && 4 ,因为状态码会由0-4。 按照下列方法进行错误处理才是合理的
function getUrl(url) { let promise = new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.onload = function() { if (xhr.status === 200) { resolve(xhr.responseText); } else { reject(new Error(xhr.statusText)); } }; xhr.onerror = function() { reject(new Error(xhr.statusText)); }; xhr.open("get", url, true); xhr.send(); }); return promise; } let url = "http://httpbin.org/get"; getUrl(url).then(res => { console.log(res); }) .catch(err => { console.error(err); });
4. promise的简写方式
- Promise.resolve() 是new promise(resolve=>{})的语法糖
- Promise.reject( new Error("出错了") )
- Promise.all()
- Promise.race()
function getURL(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open("GET", URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
let request = {
comment: function getComment() {
return getURL("http://azu.github.io/promises-book/json/comment.json").then(
JSON.parse
);
},
people: function getPeople() {
return getURL("http://azu.github.io/promises-book/json/people.json").then(
JSON.parse
);
}
};
function main() {
return Promise.all([request.comment(), request.people()]);
}
// 运行示例
main().then(function (value) {
console.log(value);
}).catch(function(error){
console.log(error);
});
5. Node的微任务(microtask)和宏任务(macrotask)
记住以下规则:
- 事件循环中每一个tick执行的顺序是, 先执行同步任务(main script), 然后执行微任务(microtack), 最后执行宏任务(macrotask)。 可以自己画张图, 将不同的任务放在不同的区域, 最后依次执行。
- 存在微任务的话,需要执行完所有的微任务, 再执行下一个宏任务
- await后面的代码会被作为一个promise返回的微任务放入到微任务队列中
- nextTick是微任务, 执行顺序先于promise
- process是微任务,的执行顺序是先于promise的
- 在使用 await 的时候我们暂停了函数,而非整段代码
2)、微任务都执行完之后,执行下一个宏任务
- 从一些事件循环的Tick来说。 Node的事件循环更复杂, 它也分为微任务和宏任务
- 微任务(microtask): promise的then回调、 promise.nextTick、 queueMicrotask
- 宏任务(macrotask): setTimeout、 setInterval、 IO事件、 setImmediate、 close事件
- Node中的事件循环不只是 微任务队列和宏任务队列(
以下队列执行顺序从上到下
)
- 微任务队列:
✅ next tick queue: process.nextTick
✅ other queue: Promise的then回调、 queueMicrotask
- 宏任务队列:
✅ timer queue: setTimeout setInterval
✅ poll queue: IO事件
✅ check queue: setImmediate
✅ close queue: close事件
6. 几道经典的异步笔试题
记住这些原则, 基本上面试题就不会有问题
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');
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
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('setTimeout0')
}, 0)
setTimeout(function () {
console.log('setTimeout2')
}, 300)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick1'));
async1();
process.nextTick(() => console.log('nextTick2'));
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
setTimeout(() => {
console.log('settimeout');
},0);
console.log('t1');
fetch('http://dict.qq.com').then(function(response){
return response.json()
}).then(function(myJson){
console.log(myJson);
}).catch(function(err){
console.log(err);
})
console.log('ferch zhihou');
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end');
}
async1()
console.log('t2');
new Promise((resolve)=>{
console.log('promise');
resolve()
}).then(()=>{
console.log('promise.then');
})
console.log('t3');
async function async2(){
console.log('async2');
}
console.log('t4');
7. async和await
- Async(异步) 函数或多或少允许你编写顺序的 JavaScript 代码,而无需将所有逻辑包装在 callbacks(回调),generators(生成器) 或 promises 中
- async 和 await 关键字基于 pormise 和 generator 做了简单的封装。本质上,它允许我们在所需的任意位置使用 await 关键字来“暂停”一个函数。