一 同步和异步、回调函数
js的执行是单线程的,如此设计的原因是如果多个线程去操作dom就会变得复杂难控;因为是单线程,所以对于耗时较长的任务如果也同步执行的话就导致阻塞,所以有了异步;<br /> js引擎执行代码的时候,对于同步任务,首先会在执行栈里面创建一个匿名函数,或者叫执行环境;然后代码先压入栈,然后执行,执行完了出栈;对于声明的函数,不会立即入栈,只有在调用的时候才会入栈,调用完毕出栈;等所有代码执行完毕,清空栈;<br /> 在有异步任务的时候,会涉及到消息队列和内部api;js引擎先执行同步的代码,执行过程中遇到异步代码,会进入到内部api中,如setTimeout,会有浏览器线程单独倒计时这个timer(浏览器线程不是js线程),timer倒计时结束,会把这个timer的回调函数放入消息队列queue中,等同步代码执行完毕,消息队列中的函数会在执行栈中执行回调函数,入栈、出栈等;异步的代码又分宏任务和微任务,事件循环会在执行栈执行完同步任务后开始工作,遍历微任务队列,有的话就放到执行栈中去执行,直到清空了微任务队列,才去遍历宏任务队列,在遍历微任务队列的过程中出现了新的微任务,会加到当前微任务队列的末尾,而不是下个任务的微任务队列;然后再宏任务队列中,有就放到执行栈中去执行,执行完毕后清空栈;<br /> 回调函数时js所有异步的根基。回调函数由调用者定义,交给执行者去执行;
二 promise
promise是es2015新增的对象,参数是个函数,函数的参数有固定的resolve和reject,分别用来传递成功后的状态和失败后的状态;promise初始是pending状态,成功以后编程fullfilled,失败以后变成rejected;一旦状态改变,promise的状态就确定了,不可再更改;
promise例子
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
if(this.status == '200') {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
});
}
ajax('apis/user2.json').then(function(res){
console.log(res)
}, function(err){
console.log(err)
});
setTimeout 转换为promise
// setTimeout(function(){
// var a = 'hello ';
// setTimeout(function(){
// var b = ' lagou ';
// setTimeout(function(){
// var c = 'I love U';
// console.log(a + b + c);
// }, 10)
// },10)
// },10);
let t = function(str) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(str)
},10)
});
}
let result = '';
t('hello').then(function(data){
result += data;
return t(' there ')
}).then(function(data){
result += data;
return t(' I love U')
}).then(function(data) {
result += data;
console.log(result)
})
promise 链式调用
promise的then返回一个全新的promise对象,所以可以链式调用;在then里面返回一个自定义新的promise也可以返回一个值;
后面的then方法就是在为上一个then返回的promise注册回调;前面then方法回调函数返回的值会作为后面then方法回调的参数;如果回调中返回的是promise,那后面then方法的回调会等待它的结束;
promise的异常处理
如果在promis执行的过程中出现了异常,就会触发reject回调;catch方法等同于then方法,只是回调的第一个参数为undefined;catch会捕获前面所有then里面的异常,但是reject只能捕获当前then里面的异常;
全局注册未捕获promise异常;浏览器中用unhandlerdreject注册到window对象,node中在process对象上注册unhandleredRejection事件;
推荐在每个promise中捕获异常,而不是到全局处理;
promise的静态方法
Promise.resove();Promise.reject()方法;通过promise.resolve包裹的promise对象回原样返回;
promise的并行方法
Promise.all([…promise]) 返回一个新的promise对象;等待所有promise结束才会执行then;
Promise.race([…promise]) 返回一个新的promise对象;等待数组中第一个promise结束就会执行then;常用来做接口超时处理;
微任务
promise、mutationObserver、nextTick会在当前任务执行完毕了立即执行;
三 generator
声明的generator函数第一次调用不会立即执行,将生成一个generator对象;后续调用对象的next方法时才会执行对于yield对应的表达式;执行一次,generator对象的指针会停留在当前语句;下次调用时会继续往下执行yield;只有遇到return或者函数结束了,才代表执行结束;此时next会返回done为true的状态;next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next时传参是无效的,v8引擎会忽略第一次next方法的参数,从二次开始,next参数才有意义;
for…of循环可以自动遍历generator函数时生成的iterator对象,且此时不需要调用next方法;
generator的throw方法,可以在函数体内抛出错误,然后再generator函数内捕获;throw方法接受一个参数,该参数会被catch语句接受,建议抛出error对象实例;generator函数体内抛出的错误,可以在函数体外捕获;在generator中执行发生错误,且没有内部捕获就不会再执行下去,如果此后还调用next会返回undefined,done为true的对象,该generator执行完毕;
generator的return方法,可以返回给定的值并且终止遍历generator函数;如果return方法不提供参数,则返回值的value是undefined;如果generator函数内部有try…finaly代码,return方法会直接让其进入finaly代码块;
next、return、throw方法本质上都是为了让generator函数继续执行,只是使用不同的语句替换yield表达式;
如果generator函数嵌套generator函数,则必须在内部的generator函数中遍历,才能依次执行,否则调用内部函数只能返回一个function;此时,用yield表达式标记内部函数,就可以依次执行;
generator案例
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
if(this.status == '200') {
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
});
}
// ajax('apis/user2.json').then(function(res){
// console.log(res)
// }, function(err){
// console.log(err)
// });
function* main() {
const user = yield ajax('/apis/user.json');
console.log(user);
}
const g = main();
const result = g.next();
result.value.then(data => {
g.next(data)
})
四 async函数
async类似与generator函数;async函数对generator函数的改进体现在: 1 内置执行器;2 更好的语义;3 更广的适用性;
五 手写Promise
1 简单同步
const PENDING = 'pending';
const REJECTED = 'rejected';
const FULFILLED = 'fulfilled';
class MyPromise {
constructor(exactor) {
exactor(this.resolve, this.reject);
}
status = PENDING;
value = undefined;
reason = undefined;
resolve = value => {
if(this.status != PENDING) return;
this.status = FULFILLED;
this.value = value;
}
reject = reason => {
if(this.status != PENDING) return;
this.status = REJECTED;
this.reason = reason;
}
then = (successCallback, failCallback) => {
if(this.status == FULFILLED) {
successCallback(this.value)
} else if(this.status == REJECTED){
failCallback(this.reason)
}
}
}
module.exports = MyPromise;
2 添加异步方法
...
successCallback = undefined;
failCallback = undefined;
resolve = value => {
...
this.successCallback && this.successCallback(this.value);
}
reject = reason => {
...
this.failCallback && this.failCallback(this.reason);
}
then = (successCallback, failCallback) => {
...
} else {
this.successCallback = successCallback;
this.failCallback = failCallback;
}
}
}
...
3 添加多个then的处理
...
successCallback = [];
failCallback = [];
resolve = value => {
...
while(this.successCallback.length) this.successCallback.shift()(this.value);
}
reject = reason => {
...
while(this.failCallback.length) this.failCallback.shift()(this.reason);
}
then = (successCallback, failCallback) => {
...
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
}
}
module.exports = MyPromise;
4 链式调用
...
then = (successCallback, failCallback) => {
let promise2 = new MyPromise((resolve, reject)=>{
if(this.status == FULFILLED) {
let x = successCallback(this.value)
resolve(x);
} else if(this.status == REJECTED){
let y = failCallback(this.reason);
reject(y);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise2;
}
}
...
5 then 返回promise的情况
...
then = (successCallback, failCallback) => {
let promise2 = new Promise((resolve, reject)=>{
if(this.status == FULFILLED) {
let x = successCallback(this.value)
resolvePromise(x,resolve, reject);
} else if(this.status == REJECTED){
failCallback(this.reason);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise2;
}
}
function resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
...
6 增加了在构造函数中发生异常、或者在then方法执行中发生异常的处理,另外增加了对then方法无参数回调的判断;
...
constructor(exactor) {
try{
exactor(this.resolve, this.reject);
}catch(error) {
this.reject(error)
}
}
...
then = (successCallback, failCallback) => {
successCallback = successCallback? successCallback: value => value;
failCallback = failCallback? failCallback: reason => {throw reason};
let promise2 = new MyPromise((resolve, reject)=>{
if(this.status == FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value)
resolvePromise(promise2, x,resolve, reject);
} catch (e) {
reject(e)
}
},0)
} else if(this.status == REJECTED){
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x,resolve, reject);
} catch (e) {
reject(e)
}
},0)
} else {
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value)
resolvePromise(promise2, x,resolve, reject);
} catch (e) {
reject(e)
}
},0)
});
this.failCallback.push(()=> {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x,resolve, reject);
} catch (e) {
reject(e)
}
},0)
});
}
});
return promise2;
}
}
...
7 promise all方法
...
static all = (array) => {
let result = [];
let index = 0;
return new MyPromise((resolve, reject) => {
function setData(key, value) {
result[key] = value;
index ++;
if(index === array.length) {
resolve(result)
}
}
for (let i = 0; i < array.length; i++) {
if (array[i] instanceof MyPromise) {
// promise
array[i].then(value => {
setData(i, value)
}, reason => {
reject(reasonW)
});
} else {
// 非promise
setData(i, array[i]);
}
}
});
};
}
...
8 promise resolve方法
...
static resolve(value) {
if(value instanceof MyPromise) return value;
return new MyPromise(resolve => {resolve(value)})
}
...
9 promise finally方法
...
finally(finallyCallback){
return this.then(value => {
finallyCallback();
return value;
}, reason => {
finallyCallback();
throw reason;
})
}
...
10 finally方法返回promise
...
finally(finallyCallback){
return this.then(value => {
return MyPromise.resolve(finallyCallback()).then(() => value)
}, reason => {
return MyPromise.resolve(finallyCallback()).then(()=> { throw reason });
})
}
...
11 catch 方法
...
catch(failCallback) {
return this.then(undefined, failCallback)
}
...