We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
我们首先来实现一个简单版的Promise,让他支持下面的简单用法
Promise
const p = new Promise((resolve, reject) => { console.log('executor') resolve('成功了') }) p.then((value) => { console.log('状态是' + value) }, (reason) => { console.log('状态是' + reason) })
::: tip 这里需要注意一些promise的一些简单特性,首先new Promise()里面的是同步代码,会立即执行
new Promise()
其次promise的状态一旦变为成功或者失败状态就不可以被改变了
promise
所以上诉输出的结构是 executor 状态是成功了 :::
executor
状态是成功了
接下来我们实现一款最简单的Promise
const SUCCESS = 'fulfilled' // 成功的状态 const FALL = 'rejected' // 失败的状态 const PENDING = 'pending' // 等待的状态 class Promisse { constructor (executor) { this.status = PENDING this.value = undefined // 存放当前成功的值 this.reason = undefined // 存放当前失败的值 const resolve = (value) => { if (this.status === PENDING) { this.status = SUCCESS this.value = value } } const reject = (reason) => { if (this.status === PENDING) { this.status = FALL this.reason = reason } } try { // 如果执行器的同步代码报错,进行捕获处理 executor(resolve, reject) } catch (e) { reject(e) } } then(onFullFilled, onRejected) { if (this.status === SUCCESS) { onFullFilled(this.value) } if (this.status === FALL) { onRejected(this.reason) } } } module.exports = Promisse
首先我们定义三个表示状态的常量,其次我们执行器executor对应我们new Promise(xxx)传入的xxx函数,他接收2个参数resolve和reject,这2个方法的作用是缓存当前成功的状态或者失败的状态,并且修改对应的状态为成功或失败
new Promise(xxx)
resolve
reject
然后我们需要注意的是executor执行的时候需要用try-catch包裹起来,因为同步代码也有可能报错
try-catch
最后我们需要写一个then方法,他可以根据当前promise的状态执行对应的回调函数
then
上述最简单的promise有一个小缺陷,就是不支持异步操作,如
let Promise = require('./promise1') const fs = require('fs') const p = new Promise((resolve, reject) => { fs.readFile('./name.txt', 'utf8', function(err, data) { if (err) { reject(err) } resolve(data) }) }) p.then((value) => { console.log(value) }, (reason) => { console.log(reason) }) p.then((value) => { console.log(value) }, (reason) => { console.log(reason) })
我们知道fs.readFile是一个异步操作,也就是意味着我们同步调用then方法的时候,当前promise的状态还有可能是pending状态,接下来我们实现支持异步调用
fs.readFile
pending
const SUCCESS = 'fulfilled' const FALL = 'rejected' const PENDING = 'pending' class Promisse { constructor(executor) { this.status = PENDING this.value = undefined this.reason = undefined this.onResolveCallBacks = [] // 新 this.onRejtectCallBacks = [] // 新 const resolve = (value) => { if (this.status === PENDING) { this.status = SUCCESS this.value = value this.onResolveCallBacks.forEach(fn => fn())// 将缓存起来的成功的回调遍历执行 } } const reject = (reason) => { if (this.status === PENDING) { this.status = FALL this.reason = reason this.onRejtectCallBacks.forEach(fn => fn()) // 将缓存起来的失败的回调遍历执行 } } try { executor(resolve, reject) } catch (e) { reject(e) } } then(onFullFilled, onRejected) { if (this.status === SUCCESS) { onFullFilled(this.value) } if (this.status === FALL) { onRejected(this.reason) } if (this.status === PENDING) { // 如果当前状态是等待状态,也就是可能是异步调用,我们将对应的回调存起来 this.onResolveCallBacks.push(() => { onFullFilled(this.value) }) this.onRejtectCallBacks.push(() => { onRejected(this.reason) }) } } } module.exports = Promisse
首先我们用2个变量onResolveCallBacks onRejtectCallBacks来缓存成功和失败的回调
onResolveCallBacks
onRejtectCallBacks
当我们执行then操作的时候,如果当前状态还是pending状态,那我们就需要将回调缓存起来,当异步调用了resolve或者reject的时候再将缓存遍历执行
我们开发时候最常用的写法就是链式的调用promise了,如
let Promise = require('./promise') const fs = require('fs') function read(filePath){ return new Promise((resolve, reject)=> { fs.readFile(filePath, 'utf8', function (err, data) { if (err) { return reject(err) } resolve(data) }) }) } read('./name.txt').then(function(data) { return 1000 }).then(data => { console.log(data) })
但是我们promise的链式调用和Jquery的链式调用不太一样,jq是通过返回this来达到对应的效果,而我们promise的状态一旦变为成功或者失败状态就无法进行改变,所以promise链式调用的核心在于每一个then执行之后,返回一个新的promise
::: tip 我们需要知道一些then的规则
如果then中返回一个不是promise的值,那么这个值会作为下一个then的参数
如果then中返回一个promise,那么这个promise成功或者失败的值会作为下一个then中的参数 :::
我们主要修改一下then方法,让他按照对应的规则返回一个新的promise
then(onFullFilled, onRejected) { let newPromise newPromise = new Promise((resolve, reject) => { if (this.status === SUCCESS) { setTimeout(() => { try { let x = onFullFilled(this.value) resolvePromise(newPromise, x, resolve, reject) } catch (err) { reject(err) } }) } if (this.status === FALL) { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(newPromise, x, resolve, reject) } catch (err) { reject(err) } }) } if (this.status === PENDING) { this.onResolveCallBacks.push(() => { setTimeout(() => { try { let x = onFullFilled(this.value) resolvePromise(newPromise, x, resolve, reject) } catch (err) { reject(err) } }) }) this.onRejtectCallBacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(newPromise, x, resolve, reject) } catch (err) { reject(err) } }) }) } }) return newPromise }
按照Promise A+规范,我们需要写一个resolvePromise方法来处理promise的返回值
Promise A+
resolvePromise
function resolvePromise(newPromise, x, resolve, reject) { if (newPromise === x) { // 根据A+规范 如果then中返回当前then的所属promise 应该报类型错误,避免死循环 return reject(new TypeError(`TypeErr`)) } if (typeof x === 'function' || (typeof x === 'object' && x !== null)) { try { let then = x.then if (typeof then === 'function') { //如果x是一个promise then.call(x, y => { //如果promise的结果是成功的,把他往下传 ,如果是失败的就让下一个promise也失败 resolvePromise(newPromise, y, resolve, reject) //避免调用resolve的时候传入一个promise }, err => { reject(err) }) } else { // x是一个普通对象 resolve(x) } } catch (e) { reject(e) } } else { // x是一个常量 resolve(x) } }
处理的规则大致为:
报错直接调用返回的新的promise的reject方法
如果上一个promise的值是不为promise的值,直接调用新的promise的resolve方法
然后判断上一个promise的值是不是和新的promise是否相等,想等的话会抛出一个类型错误
然后判断上一个promise的返回值是不是一个promise, 如果是就调用这个promise的方法
我们经常会看到这些代码
Premise.resolve(100).then().then().then(() => { console.log(data) // 100 })
这个其实并不复杂,我们来实现以下
then(onFullFilled, onRejected) { onFullFilled = typeof onFullFilled === 'function' ? onFullFilled : val => val // 实现值的穿透,如果传入的是函数走原来的逻辑,如果不是函数直接返回当前值 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err } // 如果传递的不是函数,则抛出错误让函数接下来捕获 let newPromise ... }
我们只需要在then的时候对传入的参数进行类型判断就好了,以对onFullFilled的处理为例子,如果不是一个函数,则将val直接返回给下一个then
onFullFilled
::: tip 其实我们看到的then().catch(e)中的catch,其实就是.then(null, fn)的语法糖,也是利用了值的传递的原理 :::
直接上代码吧
catch(cb) { return this.then(null, cb) }
Promise.resolve可以把一个值包装成一个promise,也很简单
Promise.resolve
Promise.resolve = function(value) { return new Promise((resolve, rejetc) => { resolve(value) }) }
finally的意思是无论如何promise都会走到finally里面去
finally
但是有一点需要注意的是如果finally里面返回一个promise,代码会等待这个promise执行完之后才会往下执行
new Promise((resolve, reject) => { resolve(1000) }).finally(() => { return new Promise((resolve) => { setTimeout(() => { resolve() }, 3000) }) }).then(data => { console.log(data) })
上述代码会在finally理等待3秒,然后再执行后面的then
那我们来简单实现一个finally吧
Promise.prototype.finally = function(cb) { return this.then((data) => { return Promise.resolve(cb()).then(() => data) }, (err) => { return Promise.resolve(cb()).then(() => { throw err }) }) }
主要借助了如果promise里面返回一个promise的话会等着里面的promise执行这个特性,然后也是then的一个语法糖
先简单看下Promise.all的基础用法吧
Promise.all
let fs = require('fs').promises Promise.all([fs.readFile('./name.txt', 'utf8'), fs.readFile('./age.txt', 'utf8'), 3]) .then((data) => { console.log(data) })
不管是同步代码还是异步代码,promise.all都会等待这个代码执行拿到结果,并且按照数组的顺序返回对应的结果
promise.all
那我们来简单的实现一下吧
function isPromise(value) { if (typeof value === 'function' || (typeof value === 'object' && typeof value !== null)) { if (typeof value.then === 'function') { return true } } return false } Promise.all = function (values) { return new Promise((resole, reject) => { let arr = [] let i = 0; let processData = (key, value) => { arr[key] = value if (++i === values.length) { resole(arr) } } for (let i = 0; i < values.length; i++) { let current = values[i] if (isPromise(current)) { current.then(y => { processData(i ,y) }, reject) } else { processData(i, current) } } }) }
Promise.race的用法和all有点像,不过他是如果有一个成功了,就返回这个成功的值,也就是返回最快的值,如果有一个失败了,就返回失败的值
Promise.race
all
我们直接来实现一下他吧
Promise.race = function (values) { return new Promise((resolve, reject) => { for (let i = 0; i < values.length; i++) { let current = values[i] if (isPromise(current)) { current.then(resole, reject) } else { resolve(current) } } }) }
在项目中我们经常遇到这样的需求,如果接口超过3秒没有返回,那我们就可以终止这个接口返回,其实是利用了race的特点,我们来模拟一下吧
race
let p = new Promise((resolve, reject) => { // 模拟接口请求 需要3秒 setTimeout(() => { resolve(123) }, 3000) }) // 现在我们需要在2秒的时候就不等待这个请求的结果了 function wrap(promise) { let abort let newPromise = new Promise((resolve, reject) => { abort = reject }) let p = Promise.race([newPromise, promise]) p.abort = abort return p } let p1 = wrap(p); setTimeout(() => { // 2秒的时候就不等待接口返回的结果了 p1.abort() }, 2000) p1.then(data => { console.log(data) }).catch((e) => { console.log(e) })
大致意思就是这个请求的结果我不要了,但是请求还是会发出去的
The text was updated successfully, but these errors were encountered:
No branches or pull requests
实现符合A+规范的Promise
实现简易版的Promise
我们首先来实现一个简单版的
Promise
,让他支持下面的简单用法::: tip
这里需要注意一些promise的一些简单特性,首先
new Promise()
里面的是同步代码,会立即执行其次
promise
的状态一旦变为成功或者失败状态就不可以被改变了所以上诉输出的结构是
executor
状态是成功了
:::
接下来我们实现一款最简单的
Promise
首先我们定义三个表示状态的常量,其次我们执行器
executor
对应我们new Promise(xxx)
传入的xxx函数,他接收2个参数resolve
和reject
,这2个方法的作用是缓存当前成功的状态或者失败的状态,并且修改对应的状态为成功或失败然后我们需要注意的是
executor
执行的时候需要用try-catch
包裹起来,因为同步代码也有可能报错最后我们需要写一个
then
方法,他可以根据当前promise
的状态执行对应的回调函数支持异步调用
上述最简单的
promise
有一个小缺陷,就是不支持异步操作,如我们知道
fs.readFile
是一个异步操作,也就是意味着我们同步调用then
方法的时候,当前promise
的状态还有可能是pending
状态,接下来我们实现支持异步调用首先我们用2个变量
onResolveCallBacks
onRejtectCallBacks
来缓存成功和失败的回调当我们执行
then
操作的时候,如果当前状态还是pending
状态,那我们就需要将回调缓存起来,当异步调用了resolve
或者reject
的时候再将缓存遍历执行实现then的链式调用
我们开发时候最常用的写法就是链式的调用
promise
了,如但是我们
promise
的链式调用和Jquery的链式调用不太一样,jq是通过返回this来达到对应的效果,而我们promise
的状态一旦变为成功或者失败状态就无法进行改变,所以promise
链式调用的核心在于每一个then
执行之后,返回一个新的promise
::: tip
我们需要知道一些then的规则
如果then中返回一个不是promise的值,那么这个值会作为下一个then的参数
如果then中返回一个promise,那么这个promise成功或者失败的值会作为下一个then中的参数
:::
我们主要修改一下
then
方法,让他按照对应的规则返回一个新的promise
按照
Promise A+
规范,我们需要写一个resolvePromise
方法来处理promise的返回值处理的规则大致为:
报错直接调用返回的新的
promise
的reject
方法如果上一个
promise
的值是不为promise
的值,直接调用新的promise
的resolve
方法然后判断上一个
promise
的值是不是和新的promise
是否相等,想等的话会抛出一个类型错误然后判断上一个
promise
的返回值是不是一个promise
, 如果是就调用这个promise
的方法实现值的穿透
我们经常会看到这些代码
这个其实并不复杂,我们来实现以下
我们只需要在
then
的时候对传入的参数进行类型判断就好了,以对onFullFilled
的处理为例子,如果不是一个函数,则将val直接返回给下一个then
::: tip
其实我们看到的then().catch(e)中的catch,其实就是.then(null, fn)的语法糖,也是利用了值的传递的原理
:::
直接上代码吧
实现Promise.resolve
Promise.resolve
可以把一个值包装成一个promise
,也很简单实现finally
finally
的意思是无论如何promise
都会走到finally
里面去但是有一点需要注意的是如果
finally
里面返回一个promise
,代码会等待这个promise
执行完之后才会往下执行上述代码会在
finally
理等待3秒,然后再执行后面的then
那我们来简单实现一个
finally
吧主要借助了如果
promise
里面返回一个promise
的话会等着里面的promise
执行这个特性,然后也是then
的一个语法糖实现Promise.all
先简单看下
Promise.all
的基础用法吧不管是同步代码还是异步代码,
promise.all
都会等待这个代码执行拿到结果,并且按照数组的顺序返回对应的结果那我们来简单的实现一下吧
实现Promise.race
Promise.race
的用法和all
有点像,不过他是如果有一个成功了,就返回这个成功的值,也就是返回最快的值,如果有一个失败了,就返回失败的值我们直接来实现一下他吧
终止promise
在项目中我们经常遇到这样的需求,如果接口超过3秒没有返回,那我们就可以终止这个接口返回,其实是利用了
race
的特点,我们来模拟一下吧大致意思就是这个请求的结果我不要了,但是请求还是会发出去的
The text was updated successfully, but these errors were encountered: