Promise分享

news/2024/11/16 18:54:43/

手写promise之前需要知道

  1. 宏任务 & 微任务
    我们都知道 Js 是单线程的,但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)。

JS异步的实现靠的就是浏览器的多线程,当他遇到异步API时,就将这个任务交给对应的线程,当这个异步API满足回调条件时,对应的线程又通过事件触发线程将这个事件放入任务队列,然后主线程从任务队列取出事件继续执行。以浏览器为例,有以下几个常见的进程和线程:
在这里插入图片描述

在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
宏任务与微任务的几种创建方式 👇

宏任务(Macrotask)微任务(Microtask)
setTimeoutrequestAnimationFrame(有争议)
setIntervalMutationObserver(浏览器环境)
MessageChannelPromise.[ then/catch/finally ]
I/O,事件队列process.nextTick(Node环境)
setImmediate(Node环境)queueMicrotask
script(整体代码块)

来道面试题:

async function async1 () {console.log('async1 start');await async2();console.log('async end')
}
async function async2 () {console.log('async2')
}
console.log('script start')
setTimeout(() => {console.log('serTimeout')
}, 0)
async1()
new Promise((function (resolve) {console.log('promise1')resolve()
})).then(function () {console.log('promise2')
}).then(function () {console.log('promise3')
}).then(function () {console.log('promise4')
})
console.log('script end')

queueMicrotask的用法

console.log('script start');
new Promise((resolve) => {console.log('pr start');resolve();
}).then(res => {console.log('pr then');
}) .then(res => {console.log('pr end');
})
queueMicrotask(() => {console.log('queueMicrotask');
})
console.log('script end');
  1. 什么是Promise A+规范
    当别人问起来什么是Promise/A+规范,可能会觉得有点懵,你可能用过Promise,但很可能不了解什么是Promise规范。
    其实Promise 规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6中采用了 Promise/A+ 规范。
  2. Promise标准解读
    1. 一个promise的当前状态只能是pending、fulfilled和rejected三种之一。状态改变只能是pending到fulfilled或者pending到rejected。状态改变不可逆。
    2. promise的then方法接收两个可选参数,表示该promise状态改变时的回调(promise.then(onFulfilled, onRejected))。then方法返回一个promise,then 方法可以被同一个 promise 调用多次。
    3. Promise/A+并未规范race、all、catch方法,这些是ES6自己规范的。

正式开始

  1. 确定一个异步方法
微任务(Microtask)
requestAnimationFrame(有争议)
MutationObserver(浏览器环境)
Promise.[ then/catch/finally ]
process.nextTick(Node环境)
queueMicrotask

在这里插入图片描述

其中不需要判断环境的,也就queueMicrotask了,但是queueMicrotask的兼容性不是很好,所以写一个兼容方法:

const asyncFn = function () {if (typeof queueMicrotask === 'function') {return queueMicrotask;}if (typeof process === 'object' && process !== null && typeof (process.nextTick) === 'function') {return process.nextTick;}if (typeof (setImmediate) === 'function') {return setImmediate}return setTimeout
}()
  1. 写一个基础的Promise
const pr = new Promise((resolve, reject) => {const number = Math.random();if (number > 0.5) {resolve(number);} else {reject(new Error(number));}
});const STATUS_PADDING = Symbol('PADDING');
const STATUS_FULFILLED = Symbol('FULFILLED');
const STATUS_REJECTED = Symbol('REJECTED');class MiniPromise {constructor(callback){try {        callback(this.onFulfilled.bind(this), this.onRejected.bind(this));} catch (error) {this.onRejected(error);}}value = null;status = STATUS_PADDING;onFulfilled(value) {this.status = STATUS_FULFILLED;this.value = value;}onRejected(error) {this.status = STATUS_REJECTED;this.value = error;}
}
  1. 实现then
pr.then(res => {console.log('success::', res);
}, err => {console.error('error:::', err);
});// 调用then中的方法时,需要判断当前promise的状态
// 如果是padding状态,需要等待promise的状态更新为fulfilled/rejected时才能调用
// 所以需要保存一下callback,等更新后再调用这个方法。
then(resolve, reject) {// 平常使用的时候,一般只传递了第一个参数,没有传reject方法,所以给他们加上默认值resolve = typeof resolve === 'function' ? resolve : value => value;reject = typeof reject === 'function' ? reject : value => value;const newPromise = new MiniPromise(() => {});switch(this.status) {case STATUS_PADDING:this.resolveCallback = resolve;this.rejectCallback = reject;this.thenPromise = newPromise;break;case STATUS_FULFILLED:try {newPromise.onFulfilled(resolve(this.value));} catch(err) {this.status = STATUS_REJECTED;newPromise.onRejected(reject(err));}break;case STATUS_REJCETED:try {newPromise.onRejected(reject(this.value));} catch(err) {newPromise.onRejected(reject(err));}break;}return newPromise;
}
  1. 多个then调用
const pr2 = pr.then(res => {console.log('success:222:', res);
}, err => {console.error('error:222:', err);
});
const pr3 = pr.then(res => {console.log('success:333:', res);
}, err => {console.error('error:333:', err);
});

想想改咋改?

// 将this.rejectCallback改为数组就好了onFulfilled(value) {this.status = STATUS_FULFILLED;this.value = value;let index = 0;while(this.resolveCallback.length) {const item = this. resolveCallback.shift();try {const result = item(this.value);this.thenPromiseList[index]. onFulfilled(result);} catch(err) {console.log('xxx resolve:::::', err);this.thenPromiseList[index].onRejected(err);}index ++;}
}
onRejected(error) {this.status = STATUS_REJECTED;this.value = error;let index = 0;while(this.rejectCallback.length) {const item = this.rejectCallback.shift();try {const result = item(this.value);this.thenPromiseList[index].onRejected(result);} catch(err) {console.log('xxx resolve:::', err);this.thenPromiseList[index].onRejected(err);}index ++;}
}then(resolve, reject) {// ......switch(this.status) {case STATUS_PADDING:this.resolveCallback = resolve;this.rejectCallback = reject;this.resolveCallback.push(resolve);this.rejectCallback.push(reject);this.thenPromiseList.push(newPromise)break;// .....}
}
  1. 实现错误catch
catch(onRejected) {if (typeof onRejected !== 'function') {onRejected = reason => reason;}const newPromise = new MiniPromise(() => {});switch(this.status) {case STATUS_REJECTED:newPromise.status = this.status;newPromise.value = onRejected(this.value);break;case STATUS_PADDING:this.rejectCallbackList.push(onRejected);this.thenPromiseList.push(newPromise);break;}return newPromise;
}
  1. 实现直接调用resolve和reject
// 一般来说,写在class中只有属性和方法.都可以被实例化的对象进行调用,比如:
class Animal {type = '动物'name = 'animal'
}
const dog = new Animal();
dog.type // 动物
dog.name // animal
// 假如想要在外部直接使用class内部的方法,或属性,比如这样:
class Person {name = "shen"
}
console.log(Person.name); // 这样会打印Person1【类的名称】,并取不到shen// 这时就需要使用static关键字
class Person {static name = 'shen';
}
console.log(Person.name);    // 这样就可以得到 shen 了
// 静态的方法中也可以使用同一个类下的其他静态属性或方法
class Person2 {static hhha = "aaa"static sayHi() {console.log(this.hhha + ' hi~');}
}
Person2.sayHi() // aaa hi~
// 如果hhha 不是static的,那么sayHi会打印出 undefined hi~// react中的getDerivedStateFromProps,也是一个静态方法

回归正题,想要实现直接调用resolve,和reject方法,也需要使用static关键字

// 先看看使用方法
const resPr = Promise.resolve(123);class MiniPromise {// ...static resolve(value) {const newPr = new MiniPromise((resolve, reject) => {try {resolve(value);} catch(err) {reject(err);}})return newPr;}    static reject(value) {const newPr = new MiniPromise((resolve, reject) => {try {reject(value);} catch(err) {reject(err);}})return newPr;}// ...
}

整体看一下代码

class MiniPromise {constructor(callback) {try {callback(this.handleFulfilled.bind(this), this.handleRejected.bind(this));} catch(err) {this.handleRejected(err);}}status = STATUS_PADDING;value = undefined;rejectCallbackList = [];fulfillCallbackList = [];thenPromiseList = [];static resolve(value) {const newPromise = new MiniPromise((resolve, rejcet) => {try {console.log(value);resolve(value);} catch(err) {rejcet(err);}});return newPromise;}static reject(value) {const newPromise = new MiniPromise((resolve, reject) => {try {reject(value);} catch(err) {reject(err);}});return newPromise;}handleFulfilled(value) {this.status = STATUS_FULFILLED;this.value = value;console.log('resolve', value, this.fulfillCallbackList);let index = 0;while(this.fulfillCallbackList.length) {const item = this.fulfillCallbackList.shift();try {const result = item(this.value);if (result === this.thenPromiseList[index]) {this.thenPromiseList[index].handleRejected(new TypeError('Chaining cycle detected for promise #<Promise>'));continue};this.thenPromiseList[index].handleFulfilled(result);} catch(err) {console.log('xxx resolve:::::', err);this.thenPromiseList[index].handleRejected(err);}index ++;}}handleRejected(reason) {this.status = STATUS_REJECTED;this.value = reason;console.log('rejcet', reason);let index = 0while(this.rejectCallbackList.length) {const item = this.rejectCallbackList.shift();try {const result = item(this.value);if (result === this.thenPromiseList[index]) {this.thenPromiseList[index].handleRejected(new TypeError('Chaining cycle detected for promise #<Promise>'))continue};this.thenPromiseList[index].handleRejected(result);} catch(err) {this.thenPromiseList[index].handleRejected(err);}index ++;}}then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;onRejected = typeof onRejected === 'function' ? onRejected : reason => reason;const newPromise = new MiniPromise(() => {});switch(this.status) {case STATUS_FULFILLED:try {newPromise.status = this.status;newPromise.value = onFulfilled(this.value);} catch(err) {console.log('errr', err);newPromise.value = onRejected(err);}break;case STATUS_REJECTED:newPromise.status = this.status;try {newPromise.value = onRejected(this.value);} catch(err) {newPromise.value = onRejected(err);}break;case STATUS_PADDING:this.rejectCallbackList.push(onRejected);this.fulfillCallbackList.push(onFulfilled);this.thenPromiseList.push(newPromise);break;}return newPromise;}catch(onRejected) {if (typeof onRejected !== 'function') {onRejected = reason => reason;}const newPromise = new MiniPromise(() => {});switch(this.status) {case STATUS_REJECTED:newPromise.status = this.status;newPromise.value = onRejected(this.value);break;case STATUS_PADDING:this.rejectCallbackList.push(onRejected);this.thenPromiseList.push(newPromise);break;}return newPromise;}
}

http://www.ppmy.cn/news/892758.html

相关文章

easyx赛车小游戏(新手小白)

2021新年快乐呀&#xff0c;两天都没出去玩终于写完这个小游戏。 之前上一篇博文说过要写跑酷来着&#xff0c;但因为我实在太菜了就中途放弃了&#xff0c;换成了公路障碍赛车。。。 界面如图&#xff0c;感觉是个比较劣质的游戏。。。但是它还是一个有碰撞判断&#xff0c…

C/C++游戏项目教程:《雷霆战机》

《雷霆战机》是以太空科幻世界观&#xff0c;12星座关卡模式和弹幕联袂上演掌上“星球大战”。百种战机&#xff0c;千种弹幕&#xff0c;酷炫装备&#xff0c;强劲暴走&#xff0c;演绎别样星海激情。在继承了经典飞行射击游戏爽快体验的同时&#xff0c;更添加了革命性的创新…

carx2服务器维护不足什么问题,carx2赛车漂移

carx2赛车漂移玩起来手感超级棒,漂移的时候感觉非常刺激,地图蛮大的赛道外那些跑道好玩,地图数量不怎么多,而且大部分都要金币!我存了20多万,然后买新车,改装也花了很多钱! carx2赛车漂移介绍 很喜爱carx2赛车漂移这款游戏。最先,重中之重,把自动刹车关掉,或是降低,…

Unity3D教程:赛车漂移算法

漂移的解决方案。 方案1.整个赛车干脆都用translate做坐标移动&#xff0c;这个就是无视wheelcollider物理引擎的做法&#xff0c;新手适用&#xff0c;简单方便&#xff0c;但之后的开发上会有很多问题需要解决&#xff08;如果你想解决这些问题的话&#xff09;。 方案2.用…

疯狂的赛车下载

剧情简介 黄渤饰演的自行车车手耿浩&#xff0c;因为0.01秒落败&#xff0c;成为银牌车手&#xff0c;这是一条线&#xff1b;第二条线是大贼二贼&#xff0c;他们阴差阳错成了贼&#xff0c;最后还绑票伤人&#xff1b;第三条线是台湾来的黑帮四人组&#xff0c;他们意图在内…

Unity3D赛车游戏+脚本基础

前言 游戏对象实例化 Scenes游戏场景 GameObject游戏对象 Component组件 Component使用方法 预制体 Unity事件函数 Instantiate():实例化对象 什么是Time.deltaTime Transform的移动&#xff0c;旋转和缩放 实战:赛车游戏 运行演示 具体步骤 游戏打包流程 前言 …

控制台版 赛车游戏

视频&#xff1a;https://www.bilibili.com/video/BV14z4y1r7wX?p4&t717 代码&#xff1a;https://github.com/OneLoneCoder/videos/blob/master/OneLoneCoder_RetroArcadeRacer.cpp 目录 一、预览图 二、代码过程 三、完整代码 一、预览图 左图是做出来的效果&#xff…

html实现经典赛车小游戏

文章目录 1.设计来源1.1 主界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130580123 html实现经典赛车小游戏源码 html实现经典赛车小游戏源码&#xff0c;1.通过键盘…