Comments (30)
ES6 在 Promise/A+ 规范基础上扩展了一些功能,例如:resolve 解析 promise 和 thenable 对象,根据一些测试用例做了补充和修改(node环境),感兴趣可以参考 https://github.com/lfpdev/promise
from blog.
大佬真棒!学习的榜样!
from blog.
棒棒哒
from blog.
顶礼膜拜,看多了都是泪😭
from blog.
小姐姐棒棒的,今天看了你的这篇promise,自己试着写了一遍
from blog.
小姐姐棒棒的,今天看了你的这篇promise,自己试着写了一遍
from blog.
看小姐姐的文章能找到好工作
from blog.
good
from blog.
let x = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(2);
})
);
}).then(res => {
console.log(res);
});
我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗
from blog.
resolvePromise中self是多余的,好几次以为是自身对象,debug走一遍之后发现是window,再往下面看的时候发现并没有用到。
from blog.
let x = new Promise((resolve, reject) => { resolve( new Promise((resolve, reject) => { resolve(2); }) ); }).then(res => { console.log(res); });我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗
// 1) Promise 是一个类或者一个方法
// 2) Promise 有三种状态 pending (等待态) fulfilled (成功态) rejected (失败态)
// 默认为pending态
// 只有是pending态的时候才能更改状态为fulfilled 或者 rejected 状态一旦更改就不允许再次被修改
// 3) Promise 内部提供一个成功的value, 一个失败的reason
// 4) Promise 内部提供两个方法 一个resolve 一个reject
// 如果两个方法都不传 默认返回undefined
// resolve或者reject不能抛异常 代码会直接报错
// resolve接收成功的value reject接收失败的reason
// 调用resolve 会把状态从 pending 更改为 fulfilled
// 调用reject 会把状态从 pending 更改为 rejected
// 如果在executor 执行器中异步调用resolve或者reject 例如setTimeout调用
// 如果在异步任务重抛出异常 则会直接执行这个throw抛出的 不会走入到下面的then方法的回调了
// then回调时候状态依然是pending态 需要发布订阅模式处理 将订阅的存放在数组里面
// 为什么存放的是一个数组呢 应为一个实例 可以then多次 一个多次订阅
// 成功的onFulfilledCallBacks 失败的onRejectedCallBacks
// 5) Promise 接收一个 executor 执行器, 默认会立即执行
// 第一个参数就是内部提供的resolve 第二个参数就是内部提供的参数reject
// 多个resolve跟reject嵌套的话 应该以最后一个的作为结果
// 6) Promise 有个then函数 默认有两个参数onFulfilled 和 onRejected
// onFulfilled resolve或者reject调用之后返回的是一个普通值或者一个新的promise调用的resolve的值
// 将value作为参数传递给onFulfilled
//
// onRejected resolve和reject调用之后的回调返回一个promise的reject的值或者抛出异常 将reason作为参数传递给onRejected
// then有返回值
// catch的特点是 如果都没有错误处理 一层层找 没有找到错误处理 会找最近的catch catch也是then
// promise中实现链式调用 返回一个新的promise
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 判断x的状态 是让promise2 变成 成功态 还是失败
// 此方法为了兼容所有的promise库 尽可能详细 不出错
function resolvePromise(promise2, x, resolve, reject) {
/** PromiseA+ 2.3.2 不能引用同一个对象 会造成死循环*/
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #'));
}
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
// x如果是对象或者函数 说明他有可能是个对象
/** PromiseA+ 2.3.2.2
* 取之可能会报错 then方法可能是由getter定义的
* Object.defineProperty(promise, 'then', {
* get () {
* throw new Error();
* }
* })
* /
let called;
try {
let then = x.then;
if (typeof then === 'function') {
/* PromiseA+ 2.3.2.2
* let obj = {
* index: 0,
* get then () {
* if (++this.index=== 2) throw error
* }
* }
* obj.then 就会执行
/
// 相当于传进来的值是reject
then.call(
x,
(y) => {
// PromiseA+ 2.3.3.3
// 防止一个promise调用多次 走完成功再走失败 或者走完失败 再走成功 或者一直走成功 或者一直走失败
if (called) return
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
// PromiseA+ 2.3.3.3
if (called) return
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
// PromiseA+ 2.3.3.3
if (called) return;
called = true;
reject(e);
}
} else {
//这里有问题写的
// 普通值 直接成功就好了
resolve(x);
}
}
function isPromise (x) {
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let x = x.then
if (typeof x.then === 'function') {
return true
}
return false
} catch (error) {
return false
}
}
return false
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallBacks = [];
this.onRejectedCallBacks = [];
let resolve = (value) => {
if (value instanceof Promise) {
// 可能我们初始化的时候传进来的就是个Promise
// 递归解析 直到是普通值为止
// console.log(value)
value.then(resolve, reject);
return false
}
if (this.status === PENDING) {
//PromiseA+ 2.1.1
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallBacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
//PromiseA+ 2.1.1
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallBacks.forEach((fn) => fn());
}
};
try {
//PromiseA+ 1.1.4
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === 'function'
? onRejected
: (err) => {
throw err;
};
// PromiseA+ 2.2.7
// 为了实现链式调用 创建一个新的promise
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// 执行then中的方法 可能返回的是一个普通值 或者promise执行
// 我要判断x的类型是不是一个promise 如果是promise的话 我要让这个promise执行
// 并采用他的状态 作为promise的成功或者失败
/* PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onFulfilled(this.value);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
let x = onRejected(this.reason);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onFulfilledCallBacks.push(() => {
// 切片编程
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onFulfilled(this.value);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallBacks.push(() => {
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onRejected(this.reason);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
// 切片编程
});
}
});
return promise2;
}
catch (errCallBack) {
return new Promise((resolve, reject) => {
resolve(this.then(null, errCallBack));
});
}
finally (callback) {
return this.then(
value => new Promise.resolve(callback()).then(() => value),
reason => new Promise.resolve(callback()).then(() => { throw reason })
);
}
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let idx = 0;
let arr = []
function processData(y, index){
arr[index] = y
if (arr.length === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
let currentValue = promises[i]
idx++
if (isPromise(currentValue)) {
currentValue.then((y) => {
processData(i)
},reject)
} else {
processData(currentValue, i)
}
}
})
}
Promise.race = function race(entries) {
var Constructor = this; // this 是调用 race 的 Promise 构造器函数。
if (!isArray(entries)) {
return new Constructor(function (_, reject) {
return reject(new TypeError('You must pass an array to race.'));
});
} else {
return new Constructor(function (resolve, reject) {
var length = entries.length;
for (var i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}
// Promise.resolve 和 Promise.reject 区别
// 传入一个promise的话 resolve
Promise.resolve = function (params) {
return new Promise((resolve, reject) => {
resolve(params)
})
}
Promise.reject = function (params) {
return new Promise((resolve, reject) => {
reject(params)
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = Promise;
看下这个呢
from blog.
非常棒的一篇文章
from blog.
错的,.then执行应该在同一tick,但是你这不是同一tick
from blog.
里实现的Promise是不一致的,想问下
浏览器是 native code microtask,这毕竟是模拟的,其实是macrotask。
from blog.
好文 点赞
from blog.
个人觉得有两个地方需要改正。
第一个就是 constructor
中的 resolve
方法需要判断resolve
函数的参数是不是当前(实测:只能是当前Promise
类,不能与其他类共用)Promsie
类的实例,如果是,则调用参数的then
方法。实现如下
const resolve = data =>{
// 只能是当前Promise类的实例,其他Pormise类的实例则不可
if(data instanceof Promise){
return data.then(resolve, reject)
}
if(this.state === PENDING){
this.state = RESOLVED
this.value = data
this.onResolvedCbs.forEach(fn => fn())
}
}
第二个地方是resolvePromise
方法中的如果then
不是函数的时候,没有必要再给flag
赋值。要弄明白什么时候需要给flag
赋值,防止回调被调用多次。当把控制权交出去的时候,需要修改flag
。由于无法确定resolve
还有reject
被调用的次数,所以,需要flag
来控制回调只执行一次。当then
不是函数的时候,就代表不是promise
,就不再需要修改flag
了。
from blog.
2.3.2 x如果是一个Promise,代码中怎么没体现出来?
from blog.
@YvetteLau 想确认下
关于Promise.resolve的实现是不是对于thenable对象处理缺少了try catch
如果这样的话
Promise.resolve({ then: function() { throw new Error(11111) } }).then(res => console.log(1, res), err => console.log(err, 5555))
会不会有问题
from blog.
finally 那边是不是可以直接这样写:
Promise.prototype.finally = function (onFinally) {
return this.then(onFinally, onFinally)
}
from blog.
finally 那边是不是可以直接这样写:
Promise.prototype.finally = function (onFinally) { return this.then(onFinally, onFinally) }
不行哈,比如你看这样的代码
Promise.resolve(2).finally().then((val) => {
console.log(val);
})
finally不会影响promise一个状态的传递,你这个代码如果onFinally有返回值,就会影响到后面then的回调
from blog.
源码复制后报错 @YvetteLau
TypeError: adapter.deferred is not a function at adapter.resolved (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\programmaticRunner.js:13:29) at Object.exports.a fulfilled promise (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\helpers\reasons.js:51:12) at C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:58:53 at Array.forEach (<anonymous>) at Suite.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:57:30) at context.describe.context.context (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\node_modules\mocha\lib\interfaces\bdd.js:47:10) at Suite.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:30:5) at context.describe.context.context (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\node_modules\mocha\lib\interfaces\bdd.js:47:10) at Object.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:15:1) at Module._compile (internal/modules/cjs/loader.js:778:30)
from blog.
好像对 'thenable' 的参数不适用?
from blog.
有个问题
this.value如果是个promise对象的话 第一个then拿到的永远是reslove的回调,结果是这个promise对象
实际应该是要根据promise的状态来判断是resolve还是reject.以及是value还是reason.
from blog.
2.3.2 x如果是一个Promise,代码中怎么没体现出来?
promise 本身就是一种 thenable,下面统一处理了,效果一样的,只是没有按照规范那样实现
from blog.
resolvePromise中self是多余的,好几次以为是自身对象,debug走一遍之后发现是window,再往下面看的时候发现并没有用到。
确实用不到,resolvePromise 是在全局中声明的函数,this 指向的是全局对象,除非传 promise 实例给 resolvePromise,否则它根本拿不到 promise 实例
from blog.
``> ```js
let x = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(2);
})
);
}).then(res => {
console.log(res);
});我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗
确实,我也发现了这个问题,然后去网上找 Promise 实现,发现很多手写 Promise 实现都没有考虑给 resolve 函数传另一个 promise 对象的情况。
const p1 = new Promise((resolve, _reject) => {
setTimeout(resolve, 1000, "p1");
});
const p2 = new Promise((resolve, _reject) => {
resolve(p1);
});
p2.then((res) => {
console.log(res);
});
对于 es6 的 Promise,输出的是:p1;
而对于楼主手写的 Promise,输出的则是:Promise { status: 'pending', onFulfilled: [], onRejected: [] }
。
最后在大佬们的回复中找到了解决方案,感谢提供解决方案的大佬们!
from blog.
里实现的Promise是不一致的,想问下
浏览器是 native code microtask,这毕竟是模拟的,其实是macrotask。
可以使用 window.queueMicroTask(callback) 来队列 microtask
from blog.
from blog.
``> ```js
let x = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(2);
})
);
}).then(res => {
console.log(res);
});我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗
确实,我也发现了这个问题,然后去网上找 Promise 实现,发现很多手写 Promise 实现都没有考虑给 resolve 函数传另一个 promise 对象的情况。
const p1 = new Promise((resolve, _reject) => { setTimeout(resolve, 1000, "p1"); }); const p2 = new Promise((resolve, _reject) => { resolve(p1); }); p2.then((res) => { console.log(res); });
对于 es6 的 Promise,输出的是:p1; 而对于楼主手写的 Promise,输出的则是:
Promise { status: 'pending', onFulfilled: [], onRejected: [] }
。 最后在大佬们的回复中找到了解决方案,感谢提供解决方案的大佬们!
我也发现了网上的实现都没处理这种这种情况, 只处理了 then 中返回 promise的情况, 如果在 new Promise的时候, 直接 resolve 了另一个 promise, 网上的实现都有问题, 但是能通过 promise a+ 测试
from blog.
from blog.
Related Issues (20)
- 【面试篇】寒冬求职季之你必须要懂的原生JS(中) HOT 7
- 【面试篇】寒冬求职之你必须要懂的Web安全
- 细说JS异步发展历程
- 【Step-By-Step】一周面试题深入解析 / 周刊 01 HOT 2
- 【Step-By-Step】一周面试题深入解析 / 周刊02
- 【Step-By-Step】一周面试题深入解析 / 周刊03
- 【Step-By-Step】一周面试题深入解析 / 周刊04 HOT 2
- 这儿有20道大厂面试题等你查收 HOT 4
- 【Step-By-Step】高频面试题深入解析 / 周刊05
- 【Step-By-Step】高频面试题深入解析 / 周刊06
- 【Step-By-Step】高频面试题深入解析 / 周刊07 HOT 2
- 【中高级前端必备】手摸手教你撸一个脚手架 HOT 1
- Proxy及其优点
- 深入理解全能的 Reducer HOT 1
- 可靠React组件设计的7个准则之SRP
- 可靠React组件设计的7个准则之封装
- eos-cli中npm link的时候报错
- React中组件逻辑复用的那些事儿
- 这些热门 GitHub 代码库,每个前端开发者都应该收藏
- to-redux2中代码运行出错 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blog.