手写promise(一)

Promise为JS的异步编程提供了一种解决方法
JS异步编程的方式还有:
1.发布订阅模式
2.回调函数
3.事件监听

先看下回调函数怎么实现异步编程

回调函数:函数当作参数进行传递, 当满足一定条件时,这个函数被执行
回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)
示例:模拟请求数据,当拿到数据后,打印出来
function getData() {}  // 耗时长
function showData() {}  // 等待前面的函数拿到数据
改写为异步形式就是下面这样
function showData(data) {
	console.log(data)
}
function getData(cb) {
	setTimeout(() => {var res = {name: 's'}; cb(res);}, 10000)
}
getData(showData)

发布订阅模式
发布者 、订阅者 、 消息服务中心
首先订阅某个事件
function showData(data) {
	console.log(data)
}
function getData() {
	setTimeout(() => {var res = {name: 's'}; publish('getdata');}, 10000)
}
1. subscribe('getdata', showData)  // 订阅getdata事件  
2. getData()

接下来看下promise
Promise 为每个异步任务返回一个promise 对象 该对象拥有一个 then()方法 此方法中允许指定回调函数
this.axios.get(url).then(res => {console.log(res)})
这里说明axios.get(url)返回了一个promise对象

可以指定多个回调
func1.then(func2).then(func3)

promise 是怎么做到当耗时任务成功或失败时通知回调

执行上下文
浏览器js代码,默认的进了全局执行环境。如果在你的全局代码中你调用了一个函数,那么顺序流就会进入到你调用的函数当中,创建一个新的执行环境并且把这个环境添加到执行栈的顶部。如果你在当前的函数中调用了其他函数,同样的事会再次发生。执行流进入内部函数,并且创建一个新的执行环境,把它添加到已经存在的执行栈的顶部。浏览器始终执行当前在栈顶部的执行环境。一旦函数完成了当前的执行环境,它就会被弹出栈的顶部, 把控制权返回给当前执行环境的下个执行环境。

JS大概有3种上下文情况
全局上下文
函数内的上下文
eval

1. 创建阶段
- 当函数被调用,但是为执行内部代码之前:
- 创建一个[作用域链]
- 创建变量,函数和参数。
- 确定this的值。

2. 激活/代码执行阶段
- 赋值,引用函数,解释/执行代码。


Promise 规范
----------------------- 1 --------------------
Terminology 术语
 1. “promise” is an object or function with a then method whose behavior
    conforms to this specification.
    promise是一个对象或者函数,它有一个 then() 方法, 它的行为符合这个规范
 2. “thenable” is an object or function that defines a then method.
 	thenable 是一个对象或函数, 它定义了一个 then() 方法
 3. “value” is any legal JavaScript value (including undefined, a
    thenable, or a promise).
    value 是一个任何合法的 Js 值 (包括undefined thenable 或者 promise )
 4. “exception” is a value that is thrown using the throw statement.
 	exception 是使用 throw 语句抛出的一个值。
 5. “reason” is a value that indicates why a promise was rejected.
	reason 是一个用于表示为什么promise被拒绝的值
----------------------- 2 ---------------------
Promise State promise状态
一个promise的状态必须是下面三种状态中的一种
pending  等待态
fulfilled  完成态
rejected  拒绝态
1.处于等待态的promise,只能转变为完成态或者拒绝态
2.处于完成态的promise,不能转变为其他任何态,且必须有一个值 value,这个值不可改变
3.处于拒绝态的promise,不能转变为其他任何态,且必须有一个原因 reason(拒绝的原因),这个原因不可改变
注:这里的不可变指的是:如果是基本值,那么不能改变,但如果是引用类型的值如对象,只需要保证引用地址不变,对象的属性值可变
---------------------- 3 -----------------------
then() 方法
一个promise 必须提供一个 then() 方法来访问它的当前值、最终值或拒绝原因
then() 方法接收两个参数 
promise.then(onFulfilled, onRejected)
1.onFulfilled 和 onRejected 都是可选的参数
1.1 如果onFulfilled不是一个函数,它必须被忽略
1.2 如果onRejected不是一个函数,它必须被忽略
2.如果onFulfilled是一个函数
2.1 在promise处于完成态时,它必须被调用 且promise的value值作为它的第一个参数
2.2 在promise未变为完成态前,它不能被调用
2.3 它只能调用一次
3.如果onRejected是一个函数
3.1 在promise处于拒绝态时,它必须被调用 且promise的reason值作为它的第一个参数
3.2 在promise未变为拒绝态前,它不能被调用
3.3 它只能调用一次
onFulfilled or onRejected must not be called 
until the execution context stack contains only platform code.
onFulfilled 或者 onRejected 不能被调用 除非执行上下文栈 仅包含平台代码

Here “platform code” means engine, environment, and promise implementation code. 
In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, 
after the event loop turn in which then is called, and with a fresh stack. 
This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick.
Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.
这里的平台代码指的是引擎,环境和promise的实施代码。
在具体的实现中,这个要求确保了onFulfilled 和 onRejected 异步执行,且是在 then() 方法调用后的那轮事件循环后的新执行栈中执行 
这可以通过宏任务机制,如 setTimeout或者setImmediate方法保证,也可以通过微任务机制如MutationObserver 或者 process.nextTick实现

--------------------事件循环---------------------

js 异步执行的运行机制:
 - 所有任务都在主线程上执行,形成一个执行栈。
 - 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
 - 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。
主线程不断重复上面的第三步。

异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。

宏任务(macrotask)::
script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
微任务(microtask):
Promise、 MutaionObserver、process.nextTick(Node.js环境)

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:
执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
更新render(每一次事件循环,浏览器都可能会去更新渲染)
重复以上步骤

将所有任务看成两个队列:执行队列与事件队列。
执行队列是同步的,事件队列是异步的,宏任务放入事件列表,微任务放入执行队列之后,事件队列之前。
当执行完同步代码之后,就会执行位于执行列表之后的微任务,然后再执行事件列表中的宏任务

示例
function a(){
	setTimeout(() => console.log(1), 0)
	console.log(2)
}
function b() {
	new Promise(() => {
		console.log(3)
		resolve(4)
	}).then(res => {
		console.log(res)
	})
}
a() 
b()
输出顺序:2341
首先执行 a() 创建a的执行上下文,压入执行栈,遇到宏任务,挂到宏任务队列,遇到同步任务输出2
执行 b() 创建b的执行上下文,压入栈,遇到同步任务,输出3,遇到微任务,挂到微任务队列
主线程此时已经清空执行队列,先去微任务队列查看,有一个,输出4,微任务队列清空 再去宏任务
队列查看,有一个宏任务,输出1



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值