1、考虑使用静态工厂方法替代构造
949 2023-04-03 03:03:23
Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。
JS作为一门单线程语言在特殊的情况下我们只能通过回调函数进行异步操作,所以我们就引入了,Promise
ajax请求、stetimeOut
同步:一定要等任务执行完了,得到结果,才执行下一个任务。异步:不等任务执行完,直接执行下一个任务。
想要创建一个promise的对象,那我们就是需要使用 new 来调用 Promise的构造器来进行实例化栗子
const promise = new Promise((resolve, reject) =>{ // 异步处理 // 处理结束后、调用resolve 或 reject if (req.status === 200) { resolve('成功'); } else { reject('失败'); } };})
在我们使用promise的时候需要会有三个状态
这三个状态让我们在调用Promise的异步方法的时候,逻辑变的更加简单
ps: 当我们在Promise的首层函数出现throw语句的时候,Promise会直接进入失败状态哦!
new Promise((resolve,reject)=>{ throw new Error('test')})
根据平常的使用经验,我们使用new操作符的时候应该是
将新创建的对象作为this的上下文对构造函数的返回值进行判断,如果该函数没有返回对象,则返回this写个简单的栗子
function create(Con, ...args){ // 创建一个空的对象 let obj = Object.create(null); // 将空对象指向构造函数的原型链 Object.setPrototypeOf(obj, Con.prototype); // obj绑定到构造函数上,便可以访问构造函数中的属性,即obj.Con(args) let result = Con.apply(obj, args); // 如果返回的result是一个对象则返回 // new方法失效,否则返回obj return result instanceof Object ? result : obj;}
测试
function company(name, address) { this.name = name; this.address = address; }
使用
var company1 = create(company, 'yideng', 'beijing');console.log('company1: ', company1);
栗子1号
var p1 = new Promise(function(resolve, reject){ // ... some coderesolve(1)});p1.then(() =>{ console.log(1)}).then(() =>{console.log(2)}) // 1 2Promise.resolve 方法,Promise.reject 方法// resolvevar p = Promise.resolve('Hello');p.then((s) =>{ console.log(s)})// reject var p1 = Promise.reject('出错了');p1.then(null, function (s){ console.log(s)});
栗子2号
var a = new Promise((resolve,reject) =>{ resolve(1)})var a1 = new Promise((resolve,reject) =>{ resolve(2)})var a3 = new Promise((resolve,reject) =>{ resolve(3)})a.then((val) =>{ console.log(val) return a1}).then((va) =>{ console.log(va) return a3}).then((v) =>{ console.log(v)})// 输出 1,2,3
// resolvevar p = Promise.resolve('Hello');p.then((s) =>{ console.log(s)})// 输出 Hello// reject var p1 = Promise.reject('出错了');p1.then(null, function (s){ console.log(s)});// 输出 出错了
该方法传入一个可迭代的数组,并返回一个promise对象,该对象在数组内所有的Promise对象执行完毕后激活
ps:如果数组内全部执行成功则返回的对象也进入成功的状态,如果有一个执行失败就进入失败状态
var a = new Promise((resolve,reject) =>{ resolve(1)})var a1 = new Promise((resolve,reject) =>{ resolve(2)})var a3 = new Promise((resolve,reject) =>{ resolve(3)})Promise.all([a,a1,a3]).then(value =>{ console.log(value)})// 输出 [1, 2, 3]
var a = new Promise((resolve,reject) =>{ resolve(1)})var a1 = new Promise((resolve,reject) =>{ resolve(2)})var a3 = new Promise((resolve,reject) =>{ reject(3)})Promise.all([a,a1,a3]).then(value =>{ console.log(value)}).catch(err =>{ console.log(err)})// 输出: 3
该方法传入一个可迭代的数组,并返回一个promise对象,该对象在数组内有第一个Promise对象执行完毕后,该方法会根据这第一个卧槽的对象的状态而改变
var a = new Promise((resolve,reject) =>{ resolve(1)})var a1 = new Promise((resolve,reject) =>{ resolve(2)})var a3 = new Promise((resolve,reject) =>{ reject(3)})Promise.race([a,a1,a3]).then(value =>{ console.log(value)}).catch(err =>{ console.log(err)})// 输出: 1
Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()是相反的。
const pErr = new Promise((resolve, reject) => { reject("总是失败");});const pSlow = new Promise((resolve, reject) => { setTimeout(resolve, 500, "最终完成");});const pFast = new Promise((resolve, reject) => { setTimeout(resolve, 100, "很快完成");});Promise.any([pErr, pSlow, pFast]).then((value) => { console.log(value); // pFast fulfils first})// 期望输出: "很快完成"
Promise和 steTimeOut 的执行顺序让我们看一下下面的代码
setTimeout(function(){ console.log('定时器开始啦') }); new Promise(function(resolve){ console.log('马上执行for循环啦'); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log('执行then函数啦') });console.log('console执行结束');
一看我就很自信的输出如下
console执行结束马上执行for循环啦执行then函数啦定时器开始啦
但是事实是:
马上执行for循环啦console执行结束执行then函数啦定时器开始啦
首先,代码执行的遇到第一个 代码块,setTimeout,setTimeout被推入 Event Table 中然后,我们会执行之后的代码 new Promise模块,在我们执行promise的回调也就是 .then函数之前,我们会先执行
console.log('马上执行for循环啦'); for(var i = 0; i < 10000; i++){ i == 99 && resolve();}
这些代码。其次,我们会执行for循环,当 i == 99 的时候,我们就会执行 resolve() 将回调函数 .then 推入 Event Queue 中,这时候我们就会执行console.log('console执行结束');
这行代码。执行完毕后,我们会发现主线程没有任务需要我们进行执行了,会去 Event Queue 读取对应的函数,进入主线程执行上述过程会不断重复,也就是常说的Event Loop(事件循环)。那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
理解一下 setTimeOut对于 setTimeOut 大家对他的第一印象就是异步可以延时执行,但是,在我们看了之前的代码之后就不能这样理解了
such as:setTimeout(() => { console.log(1)},3000)sleep(8000)
// 执行 结果// ... 2000 years later( 8s later)// 1
给大家解释一下在我们执行了 setTimeout之后,我们就会将 setTimeout 推入 Event Table 中,然后我们会开始 执行 sleep 函数, 可是这个时候sleep还在执行,只能继续等待,再过3s之后,setTimeout的计时结束, console.log(1) 被推入 Event Queue,但是sleep还在执行,只能等着sleep执行完毕之后, console.log(1) 从 Event Queue 进入主线程
ps: 我们还经常遇到 setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?答案是不会的,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。ps: HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。
进入正题除了广义的同步任务和异步任务之外,我们还有更精细的定义:宏任务和微任务:
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。
来个栗子setTimeout(function() { console.log('setTimeout');})new Promise(function(resolve) { console.log('promise');}).then(function() { console.log('then');})console.log('console');
- // promise- // console - // then - // setTimeout
事件循环,宏任务,微任务的关系如图所示: