一、单线程:
js执行环境中负责执行代码的线程只有一个
优点:安全,简单
缺点:耗时任务,等待拖延,出现假死
二、
1、单线程中的同步模式:
排队依次执行,调用栈,当某个任务执行时间过长就会延时,这叫阻塞。
2、单线程 中的异步模式:
为了解决同步中阻塞情况。
开启过后立即往后下一个任务。一般会通过回调函数的方式定义。
EventLoop(事件循环) 事件循环会监听调用栈,当调用栈中没有任务了,就会从消息队列中取出第一个回调函数,压入调用栈
事件循环是用于异步调用的重要工具。
异步调用图解:
3、回调函数,所以异步编程的根基
由调用者定义,交给执行者执行的函数
4、Promise,为异步编程提供更标准的规范。
promise是一个承诺,有三种状态,pendding,Fulfilled,Rejected。
当承诺的结果最终明确后会有自动的任务会被执行,成功的回调和失败的回调
promis的then方法的作用是为promise添加状态明确后的回调函数
第一个参数,成功回调。。
第二个参数,失败过后的回调
特点:
a、then方法返回的是一个全新promise对象。为了实现一个链条调用
b、每一个then方法是为上一个then方法返回的promise对象去添加状态明确过后回调。
c、promise的then方法中的回调函数如果返回的是一个promise对象,那下一个then方法就会等待返回的promise状态明确过后添加回调 。就是返回的这个promise的then方法中的回调,依次类推,实现了代码扁平化。
d、如果promise的then方法中的回调函数返回的是一个普通对象,那下个then方法中的回调函数的参数就是这个返回的普通值。
e、如果then方法中的回调没有返回值,默认返回的是undefind。
promise异步处理
onRejected回调函数是为promise中的异常去做处理。
5、宏任务与微任务
宏任务可以理解为在执行站中执行的任务,包括从事件队列中获取回调执行。
微任务是宏任务执行完后立即被执行的任务。
宏任务与微任务执行的关系是:
在执行站中执行的任务中,当遇到宏任务如(setimeout…),会将宏任务放入宏任务事件队列中,往后执行;当遇到微任务(promise,nextTick…)时将微任务放入微任务事件队列中。执行站中当前执行的任务全部执行完后,eventLoop将会监听异步事件队列,首先看微任务事件队列中是否有微任务,如果有把队列中的所有微任务依次执行直到没有了微任务;这时再获取到宏任务事件队列中的回调执行,这时如果有遇到微任务同样先放入微任务事件队列中,遇到宏任务向宏任务事件队列后面放入。当执行站中的任务执行完,又会执行微任务。然后再调用下一个宏任务,依次循环直到所有任务都完成。
7 、Promise
1、Promise是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2、Promise中分别有三种状态,分别为 成功 fulfilled 失败 rejected 等待pending
pending -> fulfilled
pending ->rejected
一旦状态确定就不可更改
3、resolve和reject函数是用来更改状态的
resolve: fulfilled
reject:rejected
1、手写Promise
/* 1、Promise是一个类,在执行这个类时 需要传递一个执行器 执行器会立即执行。
2、Promise中有三种状态, 分别为 成功 fulfilled 失败 rejected 等待 pending
pending -> fulfilled
pending -> rejected
状态确定了是不可逆的
3、resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
4、then方法内部做的事情就是判断状态 如果状态是成功 调用成功的回调函数 如果状态失败调用失败的回调函数 then方法是定义在原型对象上的方法
5、then方法成功状态下,执行成功函数会传递成功后的参数,失败状态下执行失败回调函数会传递失败原因参数。
6、有时不止执行一次promise的then方法,会多次执行。此时分两种情况,
如果是同步执行会依次执行,成功方法成功,失败返回失败原因。
如果是异步执行它不会依次执行,也不会等待,这时就要将依次执行的then方法中的回调都依次保存起来
7、promise中then方法可以被链式调用,并且下一个then方法的回调函数的参数,是上一个then方法回调函数执行的返回值。
8、在promise中,then方法的回调函数返回的promise对象,不能是当前then方法返回的promise,不能自己返回自己。
9、promise会捕获执行器错误
10、如果当前then方法的回调函数执行报错了,那要在下一个then方法的错误回调中捕获到。
11、在promise中可以实现 ,前面的then不传参数,这个转态会依次传递,直到有回调函数的then方法拿到结果。
12、Promise.all方法, 是用来解决异步并发问题的,它允许我们,按照异步代码调用的顺序,拿到异步代码执行的结果。
接收一个数组作为参数,这个数组中可以有普通值和promise对象,数组的顺序一定是结果的顺序
返回值也是一个promise对象,所以可以链式调用then方法
在all方法中当所有的promise是成功的最后的结果才是成功的,如果有个promise是失败结果就是失败的。
13、Promise.resolve方法, 是将给定的值转换成Promise对象,在返回的这个Promise对象当中会包裹给定的这个值
resolve方法,也可以接收一个Promise对象, 并在返回的Promise对象的then方法中拿到这个promise对象中的返回的结果。
14、Promise.finally方法
无论当前的Promise对象是成功的还是失败的,finally中的回调都会被执行一次。
在finally方法的后面可以调用then方法来拿到当前Promise对象中的返回结果
不是静态方法
在finally中回调函数中返回的Promise对象,finally方法后的then方法会等待返回的Promise执行完后的结果。
*/
// 定义状态常量。便于复用
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected' // 失败
class MyPromise {
// 在类中用constructor中接受执行器executor
constructor (executor) {
try {
// 这个执行器会立即执行、并传递两个参数resolve和reject
executor(this.resolve, this.reject)
} catch (error) {
// 当执行器执行错误后会被catch捕获,这时执行reject并返回错误信息。
this.reject(error)
}
}
// 定义这个两个参数,resolve,reject是用来改变状态的,并且是实例方法。
// 这两个方法要使用箭头函数来定义,因为要让它在被执行时的this指向点前的类的实例对象,而不是指向全局对象window
status = PENDING; // 默认当前状态为等待
// 定义两个变量用于保存成功值和失败原因
value = undefined; // 一开始成功是没有的,定义为undefined
reason = undefined; // 一开始失败是没有的,定义为undefined
// 定义实例属性,保存成功回调和失败回调
// 因为会存在多次执行then方法的时候,会有多个回调函数,所以要将属性定义为数组。
successCallback = [];
failCallback = [];
resolve = (value) => {
// 因为状态一旦确定就不可更改,此时要判断当前状态, 如果状态不是pending就要阻止程序往下执行。
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功后的值
this.value = value;
// 当异步代码完成时,如果是成功会执行resolve,此时就要执行成功回调
// 先判断成功回调是否存在,如果存在 执行, 并把成功后的值传递
// 当存在多个then方法被执行的时候会有多个成功回调,当异步代码完成时会执行resolve,依次往后取出回调来执行,直到没有回调函数了。
// this.successCallback && this.successCallback(this.value);
while(this.successCallback.length) {
// shift方法是从数组里取第一个值,并且放回取到的第一个值,所以可以直接执行这个回调,并传递成功结果。由于shift方法会改变原数组,每取一个减少一个
// 所以用数组长度来判断是否执行完了回调
this.successCallback.shift()();
}
}
reject = (reason) => {
// 因为状态一旦确定就不可更改,此时要判断当前状态, 如果状态不是pending就要阻止程序往下执行。
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
// 当异步代码完成时,如果是失败会执行resolve,此时就要执行失败回调
// 先判断失败回调是否存在,如果存在 执行, 并把失败后的原因传递
// 当存在多个then方法被执行的时候会有多个失败回调,当异步代码完成时会执行reject,依次往后取出回调来执行,直到没有回调函数了。
// this.failCallback && this.failCallback(this.reason);
while(this.failCallback.length) {
// shift方法是从数组里取第一个值,并且放回取到的第一个值,所以可以直接执行这个回调,并传递失败原因。由于shift方法会改变原数组,每取一个减少一个
// 所以用数组长度来判断是否执行完了回调
this.failCallback.shift()();
}
}
then (successCallback, failCallback) {
// then没有传参的情况下给到最后一个then,需要设置一个默认的回调函数,这个回调函数就返回当前转态。
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => {throw reason};
// 要实现then方法的链式调用,就要在原有的then方法中返回一个promise,传递一个执行器,这个执行器要被立即执行,
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 在promise2内部是拿不到promise2的,所以要将下面需要拿到promise2的代码作为异步代码,就可以拿到promise2
setTimeout(() => {
// 捕获成功回调函数的报错
try {
// 如果成功状态,执行成功回调函数,并传递成功后的值
let x = successCallback(this.value);
// 要实现then方法中的回调函数的的返回值是下个then方法的回调函数的参数,就要在下一个then方法被调用时,执行器里的resolve方法传递上一个then方法回调返回值
// resolve(x)
// promisethen方法的回调函数可以返回一个promise,这个返回的promise的结果就是下一个then方法的回调函数的参数。
// 1、判断x的值是普通值还是promise对象
// 2、如果是普通值直接调用resolve
// 3、如果是promise对象 看promise对象返回的结果
// 4、再根据promise对象返回的结果、决定调用resolve还是调用reject
// x是then方法的回调函数返回的值,promise2是then方法返回值,判断两者是否相等就知道是否自己返回了自己
// 将以上判断提取到另一个函数中
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(()=> {
// 捕获失败回调错误
try {
// 如果失败状态,执行失败回调函数, 并传递失败的原因
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else {
// 当出现异步代码时,因为不会等待异步代码而是往下执行,这时的then方法被执行,但是,resolve或者reject没有被立即执行,这时的状态为pending。
// 如果是等待状态, 当为异步代码时,会处在等待的状态,等到异步处理完成后不知道该调用成功回调还是失败回调,因此在等待状态先保存成功回调和失败回调。
// 保存回调, 因为存在多次调用then方法的情况会有多个回调,都要保存
// this.successCallback.push(successCallback);
// this.failCallback.push(failCallback);
// 当有多个回调函数的时候,也要对每个回调函数做错误处理.,这时就要保存一个函数,这个函数执行这个回调.
this.successCallback.push(() => {
// 在promise2内部是拿不到promise2的,所以要将下面需要拿到promise2的代码作为异步代码,就可以拿到promise2
setTimeout(() => {
// 捕获成功回调函数的报错
try {
// 如果成功状态,执行成功回调函数,并传递成功后的值
let x = successCallback(this.value);
// 要实现then方法中的回调函数的的返回值是下个then方法的回调函数的参数,就要在下一个then方法被调用时,执行器里的resolve方法传递上一个then方法回调返回值
// promisethen方法的回调函数可以返回一个promise,这个返回的promise的结果就是下一个then方法的回调函数的参数。
// 1、判断x的值是普通值还是promise对象
// 2、如果是普通值直接调用resolve
// 3、如果是promise对象 看promise对象返回的结果
// 4、再根据promise对象返回的结果、决定调用resolve还是调用reject
// x是then方法的回调函数返回的值,promise2是then方法返回值,判断两者是否相等就知道是否自己返回了自己
// 将以上判断提取到另一个函数中
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
// 捕获失败回调错误
try {
// 如果失败状态,执行失败回调函数, 并传递失败的原因
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
})
}
});
return promise2;
}
finally(callback) {
// 无论成功和失败,回调函数都会执行,在类中的then方法中就有成功和失败的回调, finally函数中可以再次调用then,说明返回Promise对象,
// then 方法本身就返回Promise,
return this.then(value => {
// 如果callback返回的是一个Promise,要将该promise的值传给下个then
// 不管callback返回的是否为Promise对象,都将它转为Promise对象,并将当前then方法的参数value返回,此时如果finally调用了then方法就会拿到上个then返回的value值。
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => {throw reason})
})
}
catch(failCallback) {
// catch方法是执行失败后的回调,只要让它执行then方法,第一参数为undefind。
return this.then(undefined, failCallback);
}
// all方法是被类直接调用的,所以定义为静态方法
static all (array) { //all方法接收一个数组,返回一个promise
let result = []; // 用于放回调函数返回的结果
let index = 0;
return new Promise((resolve, reject) => {
// 这里有一个问题,当执行数组中的promise对象时是异步的代码,当for循环结束后,promise还没有执行完
// 在每一次执行完数据添加后,index加一,最后判断index是否和array的长度相等。如果相等表明数组中的函数执行完了。
function addData (key, value) {
result[key] = value;
index++;
if (index === array.length) {
resolve(result);
}
}
// 将数组中的每一项判断,如果是普通值,直接返回,如果是promise对象,将结果返回。、
for (let i = 0; i < array.length; i++) {
// 记录当前值
let current = array[i];
if (current instanceof MyPromise) {
// Promise 对象
current.then(value => addData(i, value), reason => reject(reason))
} else {
// 普通值, 将值保存到数组中
addData(i, array[i]);
}
}
})
}
static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
// 如果两者相等,要执行reject,并且抛出错误,并且不能再往下执行,直接return
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
if (x instanceof MyPromise) {
// 如果是promise对象,根据then方法,如果成功调用resolve,失败调用reject, 此时的resolve和reject就是promise类中的实例方法resolve和reject,所以可以简写。
// x.then(value => resolve(value), reason => reject(reason))
x.then(resolve, reject);
} else {
resolve(x);
}
}
// 导出定义的promise,使用node的commonjs方式导出
module.exports = MyPromise;