流年似水博客开通了,本站主要是写关于Web和大数据方面内容,正在更新中,欢迎大家光临!
  1. 文章:97 篇
  2. 总浏览:69,699 次
  3. 评论:22条
  4. 最后更新:2020-06-08
  5. 分类目录:39 个

一篇文章让你吃透Promise(下篇)

Javascript l, xy 550℃ 0评论

前言:

接上篇:一篇文章让你吃透Promise(上篇) 。 本篇内容为实战,实现自定义Promise。

一、实现简单的promise

1、搭建基本架构

(function(window) {
const PENDING = "pending";
const REJECTED = "rejected";
const RESOLVED = 'resolved';
function Promise() {}
Promise.prototype.then = function(onResolved, onRejected) {}
/**
* 定义resolve方法
*/
Promise.resolve = function() {}
/**
* 定义reject方法
*/
Promise.reject = function() {}
/**
* 定义all方法
*/
Promise.all = function() {}
/**
* 定义race方法
*/
Promise.race = function() {}
window.Promise = Promise;
})(window)

2、实现构造函数

1、定义方法和字段:    

    _callbacks:存储成功和失败执行函数

_data:存储_resolve或者_reject传入值

    _status:函数状态

2、在constructor中给executor(new promise回调函数)绑定resolve、reject传入

3、使用try-catch检测,如果抛出异常,直接调用reject方法

function Promise(executor) {
this._status = "pending"; //状态
this._data = undefined; //返回值
this._callbacks = []; //储存then/catch回调函数
const that = this;
function reject() {}
function resolve() {}
try {
executor(resolve, reject); //Promise回调函数
} catch (error) { //抛出异常
reject(error)
}
}

3、实现resolve、reject

     分析:1、resolve/reject在promise方法回调函数传入两个函数(上面executor),函数会在某个时刻执行,也就意味着then或者catch的回调函数在这时执行

              2、执行resolve或者reject改变类状态,并且将值传递then或者catch的回调函数

             3、promise的状态由"PENDING"(进行中) => "RESOLVED"(已成功)或者 'PENDING'(进行中) => 'REJECTED'(已失败)

function Promise(executor) {
this._status = "pending"; //状态
this._data = undefined; //返回值
this._callbacks = []; //储存then/catch回调函数
const that = this;
function reject(val) {
if (that._status !== PENDING) return; //只有状态为等待才能执行
that._status = REJECTED;
that.data = val;
if (that._callbacks.length > 0) {
that._callbacks.forEach(item => {
setTimeout(() => {
item.onRejected(val);
})
})
}
}
function resolve(val) {
if (that._status !== PENDING) return; //只有状态为等待才能执行
that._status = RESOLVED;
that.data = val;
if (that._callbacks.length > 0) {
that._callbacks.forEach(item => {
setTimeout(() => {
item.onResolved(val);
})
})
}
}
try {
executor(resolve, reject); //Promise回调函数
} catch (error) { //抛出异常
reject(error)
}
}

4、实现then

简单实现then方法

Promise.prototype.then = function(onResolved, onRejected) {
this._callbacks.push({ onResolved, onRejected });
}

5、测试当前实现Promise

<script src="./Promise.js"></script>
<script>

var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 100)
})
p1.then((value) => {
console.log("resolved===p1-------", value)
}, (reason) => {
console.log("rejected===p1-------", reason)
})
</script>

浏览器返回:执行成功

image.png

现在promise已经能使用基本形式,但是还会有些问题,下面将会完善

二、完善promise

1、then方法完善

        分析:        

    1、当reslove/reject传入值非Promise,直接返回value

    2、当reslove/reject传入值是Promise,返回当前promise结果值

    3、如果当前状态是Pending,存储回调函数

    4、传入then回调函数(onResolved, onRejected),若未定义或不为function,去要单独处理。

          onResolved:若未定义或不为function,将继续向下传递value。

          onRejected:若未定义或不为function,将抛出异常值为reason。(上篇文章有实例)

/**
* 定义then方法,
*/
Promise.prototype.then = function(onResolved, onRejected) {
const that = this;
onResolved = typeof onResolved === "function" ? onResolved : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason; };
return new Promise((resolve, reject) => {
//判断当前状态,应对先改变状态后绑定回调函数
/**
* 1、当resolve/reject传入值非Promise,直接返回value。
* 2、当resolve/reject传入值是Promise,返回当前promise结果值
* 3、如果当前状态是Pending,存储回调函数
*/
function handle(cb) {
try {
let res = cb(that.data);
//
if (res instanceof Promise) { //当resolve/reject传入值是Promise,返回当前promise结果值
// res.then(value=> resolve(value), reason=>reject(reject));//也可这样写
res.then(resolve, reject) //简化写法
} else {
resolve(res) // 改变return的promise状态为resolved
}
} catch (error) {
reject(error) //异常处理,改变return的promise状态为rejected
}
}
if (that._status === RESOLVED) { //成功
setTimeout(() => { //这里setTimeOut代表微任务,后面有文章会讲
handle(onResolved);
})
} else if (that._status === REJECTED) { //失败
setTimeout(() => { //这里setTimeOut代表微任务,后面有文章会讲
handle(onRejected);
})
} else { //等待 如果当前状态是Pending,存储回调函数
this._callbacks.push({
onResolved(value) { //这里包一层是为了改变return的Promise的状态
handle(onResolved)
},
onRejected(reason) {
handle(onRejected);
}
});
}
})
}

 2、catch方法实现

/**
* 定义catch方法,
*/
Promise.prototype.catch = function(onRejected) {
this.then(undefined, onRejected);
}

3、Promise.reject和Promise.resove实现 (上篇有用法和实例

 这两个实现就很简单喽

/**

* 定义resolve方法
*/
Promise.resolve = function(value) {
const that = this;
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value)
}
})
}
/**
* 定义reject方法
*/
Promise.reject = function(reason) {
const that = this;
return new Promise((resolve, reject) => {
reject(reason);
})
}

4、Promise.all和Promise.race实现

分析:1、需注意all和race在循环调用resolve、reject,是没有关系的,为什么呢?因为状态只会改变一次,其余的都不会执行啦。

          2、方法里的p,即传入的数组元素,可能是Promise,但也可以不是Promise,确保可以调用then方法,使用Promise.reslove包裹。(你也可以优化下,加个判断)

/**
 * 定义all方法
*/
Promise.all = function(promise) {
if (Object.prototype.toString.call(promise) !== "[object Array]") {
throw "执行失败,请传入Array";
}
var values = new Array(promise.length); //定义数组存放返回值
var returnCount = 0; //计数器;
return new Promise((resolve, reject) => {
promise.forEach((p, index) => {
//Promise.resolve包裹p,确保p为Promise
Promise.resolve(p).then(value => {
returnCount++;
values[index] = value;
if (returnCount == promise.length) {
resolve(values);
}
}, reason => { //只要一个执行失败,整个all就返回失败
reject(reason);
})
})
})
}
/**
* 定义race方法
*/
Promise.race = function(promise) {
if (Object.prototype.toString.call(promise) !== "[object Array]") {
throw "执行失败,请传入Array";
}
return new Promise((resolve, reject) => {
promise.forEach((p, index) => {
//Promise.resolve包裹p,确保p为Promise
Promise.resolve(p).then(value => {
//返回最先执行完的值
resolve(value);
}, reason => {
reject(reason);
})
})
})
}

三、完整代码

请移驾到github观看:https://github.com/luxueyan1314/web-knowledge/tree/master/Promise

四、宏观任务和微观任务

后期更新

转载请注明:流年似水 » 一篇文章让你吃透Promise(下篇)

喜欢 (86)or分享 (0)

Warning: copy(https://cn.gravatar.com/avatar/?s=54&d=%2Fwp-content%2Fthemes%2Fyusi1.0%2Fimg%2Fdefault.png&r=g): failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in /usr/share/nginx/html/timewentby/wp-content/themes/yusi1.0/functions.php on line 239

Warning: copy(/wp-content/themes/yusi1.0/img/default.png): failed to open stream: No such file or directory in /usr/share/nginx/html/timewentby/wp-content/themes/yusi1.0/functions.php on line 243
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址