转, 原文:http://blog.csdn.net/yibingxiong1/article/details/68075416
-------------------------------------------
这是小弟的一篇开篇小作,如有不当之处,请各位道友批评指正。本文将探讨Promise的实现。

一、ES6中的Promise

1、简介

据说js很早就实现了Promise,我是不知道的,我第一次接触Promise就是在ES6中。Promise就是规定在未来达到某个状态时应该采取某种行动,而这种未来的状态是不确定的。阮一峰说Promise对象用来传递异步消息,代表了某个未来才会知道结果的事件,并为这个事件提供统一的API,供进一步处理。
Promise和事件有本质区别:Promise是用一次就扔,而事件可以被多次触发;Promise必定会产生一个信号,要么resolved(fulfilled),要么rejected,而事件可能不被触发。

2、基本用法

var p = new Promise(function(resolve){
setTimeout(function(){
resolve(111);
},1000)
}); p.then(function(value){
console.log("承诺解决了,拿到的数据为:"+value);
});
上面创建了一个promise,1秒后解决,然后用then方法添加了状态改变的回调函数。then方法中可以指定两个函数,第一个位承诺是变成resolved状态的回调函数,第二个是承诺变为rejected状态的回调函数(可省略)。也可以在catch方法中指定承诺变为rejected的函数。如下:
p.catch(function(error){
console.log("rejected callback");
})

3、Promise.all()的用法

它接受一个promise对象数组为参数,当数组中的每一个promise都变成resolved状态时,整个才算为resolved,数组中promise的返回值将组成一个数组传递给Promise.all()返回的promise的回调函数。数组中的只要有一个为rejected整个为rejected;
var p1 = new Promise(function(resolve){
resolve("p1 resolved");
});
var p2 = new Promise(function(resolve){
resolve('p2 resolved');
});
var p3 = new Promise(function(resolve){
resolve("p3 resolved");
}); var p = Promise.all([p1,p2,p3]);//这里得到了一个新的promise
p.then(function(value){
console.log("p resolved, 得到的参数为:"+value);
})
.catch(function(error){
console.log("p rejected,"+error);
});
//output:p resolved, 得到的参数为:p1 resolved,p2 resolved,p3 resolved
将p2改为rejected
var p1 = new Promise(function(resolve){
resolve("p1 resolved");
});
var p2 = new Promise(function(resolve,reject){
reject('p2 rejected');
});
var p3 = new Promise(function(resolve){
resolve("p3 resolved");
}); p = Promise.all([p1,p2,p3]);
p.then(function(value){
console.log("p resolved, 得到的参数为:"+value);
})
.catch(function(error){
console.log("p rejected,"+error);
});
//output:p rejected,p2 rejected

4、Promise.race()的用法

它和Promise.all()的参数相同,它返回的promise的状态随着promise数组中第一个状态发生改变的promise的状态而改变。
var p1 = new Promise(function(resolve){
resolve("p1 resolved");
});
var p2 = new Promise(function(resolve,reject){
reject('p2 rejected');
});
var p3 = new Promise(function(resolve){
resolve("p3 resolved");
}); p = Promise.race([p1,p2,p3]);
p.then(function(value){
console.log("p resolved, 得到的参数为:"+value);
})
.catch(function(error){
console.log("p rejected,"+error);
});
//output:p resolved, 得到的参数为:p1 resolved
我们发现虽然p2是rejected,但是p是resolved,因为第一个状态变化的是p1,而p1是resolved。如果p1是rejected,那么p一定是rejected。
上面就是ES6原生支持的Promise,那么我们该如何实现一个类似的自己的Promise呢?都原生支持了为什么还要自己实现呢?第一它是一种乐趣。第二它可以帮助我们更好的认识发布订阅模式。下面开始正式战斗!(有木有很激动,终于要开始了)。

二、实现自己的Promise

1、基础实现

function Promise(fn) {
var value = null,
deferreds = [];
this.then = function (onFulfilled) {
deferreds.push(onFulfilled);
};
function resolve(value) {
deferreds.forEach(function (deferred) {
deferred(value);
});
}
fn(resolve);
}
这个构造函数传进来一个function,声明了俩个内部变量,value传到其内部方法resolve,defferreds存储回调函数,每次调用实例的then方法会让then中的函数入队,其内部方法resolve接受一个参数,它遍历了defferreds队列,并执行其中的方法。这个内部方法被传递给了构造函数的参数fn。结合ES6中Promise的基本用法应该不难理解这段代码。其then方法相当于是一个订阅过程,resolve方法相当于一个发布过程。
var mypromise  = new Promise(function(resolve){
setTimeout(function(){
resolve("qqq");
},1000)
}); mypromise.then(function(value){
console.log(value); //qqq
})

2、问题修复

上述Promise的问题是,如果传进去的不是一个异步函数,那么resolve方法会先执行,此时还没有调用then,也就是说还没有人订阅,defferreds队列还是空的,不合预期。改进如下:
function Promise(fn) {
var value = null,
deferreds = []; this.then = function (onFulfilled) {
deferreds.push(onFulfilled);
}; function resolve(value) {
//这里将resolve放到了栈底,所以then会先执行(如果有then的话)
setTimeout(function () {
deferreds.forEach(function (deferred) {
deferred(value);
});
}, 0);
}
fn(resolve);
}

3、考虑一种情况:如果回调函数注册的很晚会怎么样

var mypromise  = new Promise(function(resolve){
resolve("qqq");
});
setTimeout(function(){
mypromise.then(function(value){
console.log(value);
})
},1000);
结果是啥也没做,为什么呢?因为当我们注册回调的时候resolve已经执行了。那可咋整呢?解决方法就是记住Promise的状态。请看代码:
function Promise(fn) {
var state = 'pending',
value = null,
deferreds = []; this.then = function (onFulfilled) {
if (state === 'pending') {
deferreds.push(onFulfilled);
return this;
}
onFulfilled(value);
return this;
}; function resolve(newValue) {
value = newValue;
state = 'fulfilled';
setTimeout(function () {
deferreds.forEach(function (deferred) {
deferred(value);
});
}, 0);
} fn(resolve);
}
就是这么简单,我们为Promise增加了一个内部变量state保存其状态,初始为pending(等待的意思),当resolve时,改变其状态为fulfilled,调then的时候做个判断,如果是pending说明resolve方法还没执行,那么我们将回调函数加到队列等待resolve即可,如果是fulfilled,说明resolve已经执行,那么我们直接执行新加入的回调函数。至于那个return this,如果你用过jquery就知道了。

4、Promise链

我们考虑这样的情况
var mypromise  = new Promise(function(resolve){
resolve("first promise");
});
mypromise.then(function(value){
console.log(value);
return new Promise(function(resolve){
resolve("second promise");
})
})
.then(function(v){
console.log(v);
})
//结果:first promise
// first promise
//为啥是两个first promise,这个是必然的,好好想想。第一个then返回的是前一个promise,而不是新创建的promise。
下面做一件有难度的是,我们来实现promise链,我看了几个小时才搞明白。不要怕,打起精神来,我们开始。
this.then = function (onFulfilled) {
return new Promise(function (resolve) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve
});
});
}; function handle(deferred) {
if (state === 'pending') {
deferreds.push(deferred);
return;
} var ret = deferred.onFulfilled(value);
deferred.resolve(ret);
} function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve);
return;
}
}
state = 'fulfilled';
value = newValue;
setTimeout(function () {
deferreds.forEach(function (deferred) {
handle(deferred);
});
}, 0);
}
then方法中返回了一个新的promise实例,在新实例中调了一个内部方法,传进去一个回调函数和一个resolve方法构成的对象;内部方法判断如果当前promise没有调resolve的话,将传入的对象入队,否则的话直接调传入的回调函数,将回调函的返回值交给resolove方法。想一下,我们的回调函数会返回什么值,可能是一个promise对象也可能是字符串或者undefined。如果说返回了一个对象,证明用户又返回了一个promise,此时我们将用户返回的promise的then拿到,然后调这个then,如果不是个对象或者函数,证明用户没有返回新的promise,此时直接resolve。

5、拒绝状态

前面讲了resolve,reject就是手到禽来了。
function Promise(fn) {
var state = 'pending',
value = null,
deferreds = []; this.then = function (onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}; function handle(deferred) {
if (state === 'pending') {
deferreds.push(deferred);
return;
} var cb = state === 'fulfilled' ? deferred.onFulfilled : deferred.onRejected,
ret;
if (cb === null) {
cb = state === 'fulfilled' ? deferred.resolve : deferred.reject;
cb(value);
return;
}
ret = cb(value);
deferred.resolve(ret);
} function resolve(newValue) {
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (typeof then === 'function') {
then.call(newValue, resolve, reject);
return;
}
}
state = 'fulfilled';
value = newValue;
finale();
} function reject(reason) {
state = 'rejected';
value = reason;
finale();
} function finale() {
setTimeout(function () {
deferreds.forEach(function (deferred) {
handle(deferred);
});
}, 0);
} fn(resolve, reject);
}

6、处理resolve和reject的回调函数异常

//改造了内部函数handle
function handle(deferred) {
if (state === 'pending') {
deferreds.push(deferred);
return;
} var cb = state === 'fulfilled' ? deferred.onFulfilled : deferred.onRejected,
ret;
if (cb === null) {
cb = state === 'fulfilled' ? deferred.resolve : deferred.reject;
cb(value);
return;
}
try {
ret = cb(value);
deferred.resolve(ret);
} catch (e) {
deferred.reject(e);
}
}
 真是无语,我已经写完了,Promise.all()和Promise.race()的实现和总结都写好了。已为添加参考文献是那个引用,按了快捷键Ctrl+Q,结果退出了浏览器,气死了。不写了,大家可以参考文章最后的参考文献,不过那个race方法的实现可能还有点问题,返回的promise的状态不是跟第一个数组中状态发生改变的promise的状态一致,而是最后一个。

三、总结

第一次写博客,突然那些写了那么多优秀文章的作者表示由衷佩服。这个Promise的实现确实是非常巧妙的,如果真的要我独自实现,恐怕还需要一些时日。不管怎样,终于完成了我的开篇之作。写作过程中对Promise的实现有了进一步认识。最后希望自己能够坚持写博客,在写作中学习。

参考文献:

【转】实现一个自己的promise的更多相关文章

  1. 手把手教你实现一个完整的 Promise

    用过 Promise,但是总是有点似懂非懂的感觉,也看过很多文章,还是搞不懂 Promise的 实现原理,后面自己边看文章,边调试代码,终于慢慢的有感觉了,下面就按自己的理解来实现一个 Promise ...

  2. 一个简单的Promise 实现

    用了这么长时间的promise,也看了很多关于promise 的文章博客,对promise 算是些了解.但是要更深的理解promise,最好的办法还是自己实现一个. 我大概清楚promise 是对异步 ...

  3. 实现一个自己的promise

    这是小弟的一篇开篇小作,如有不当之处,请各位道友批评指正.本文将探讨Promise的实现. 一.ES6中的Promise 1.简介 据说js很早就实现了Promise,我是不知道的,我第一次接触Pro ...

  4. 【JavaScript进阶】深入理解JavaScript中ES6的Promise的作用并实现一个自己的Promise

    1.Promise的基本使用 // 需求分析: 封装一个方法用于读取文件路径,返回文件内容 const fs = require('fs'); const path = require('path') ...

  5. 从源码角度实现一个自己的Promise

    原文链接:https://geniuspeng.github.io/2017/12/14/my-promise/ 关于Promise的概念以及意义就不在这里介绍了,最近看到了一些实现Promise的核 ...

  6. 彻底理解Promise对象——用es5语法实现一个自己的Promise(上篇)

    本文同步自我的个人博客: http://mly-zju.github.io/ 众所周知javascript语言的一大特色就是异步,这既是它的优点,同时在某些情况下也带来了一些的问题.最大的问题之一,就 ...

  7. 如何在外部终止一个pengding的promise对象

    今天在整理前段时间做过的项目,发现之前在集成web环信的时候遇到过一个奇怪的需求:需要终止一个正在进行等待返回的promise,或者阻止其调用resolve和reject.(具体为何会有这种需求我也不 ...

  8. 如何用原生JS实现一个简单的promise

    我又又又回来了,最近真是累的跟狗一样,急需一个大保健回复一下子精力 我现在是一边喝着红牛一边写着博客,好了好了,不扯了,回归整体好吧 先简单来说一下啥是promise吧 它是什么?Promise是一个 ...

  9. 如何实现一个串行promise

    异步执行任务A.B.C,...... 1.使用数组的reduce方法,reduce里有四个参数,pre,next,index,arr, 2.如果then方法里返回的是一个promise对象,那么执行下 ...

随机推荐

  1. postgresql update from

    1,update   from   关联表的更新 update table a set name=b.name from table B b  where a.id=b.id; update test ...

  2. 【转】用jquery编写动态的返回顶部特效

    jquery代码: function gotoTop(min_height){ //预定义返回顶部的html代码,它的css样式默认为不显示 var gotoTop_html = '<div i ...

  3. $("[lay-id='demo'] tbody tr[data-index=0]") 查找某行layui table

    $("[lay-id='demo'] tbody tr[data-index=0]")

  4. 新进Linux菜鸟,请多多关照

    早早知晓Linux的大名,一直未研究学习,近来看了kernel一些源代码,在网上搜过很多基础的知识.感觉这个Linux的世界很广大,值得好好深入学习.初生婴儿,呱呱落地,必先躺若干日后能坐,在学爬,进 ...

  5. web环境搭建

    [服务器] 硬件设备---计算机 软件 [作用] 作为web服务器运行.可以管理web项目 [目录说明] bin :存放各类可以执行文件,如:startup.bat conf:存放各类配置文件,常用配 ...

  6. lua报错,看到报错信息有tail call,以为和尾调用有关,于是查了一下相关知识

    尾调用是指在函数return时直接将被调函数的返回值作为调用函数的返回值返回,尾调用在很多语言中都可以被编译器优化, 基本都是直接复用旧的执行栈, 不用再创建新的栈帧, 原理上其实也很简单, 因为尾调 ...

  7. DP 简单题目练习

    ZOJ 1234 这道题目我表示也还不是特别能理解....还是太菜了T T 从后往前思考,因为只要后面有多的数在,那么C肯定是存在的,只要考虑是否把前两个数加在一起作为badness值这样两种情况来考 ...

  8. 【状压+状态转移】A Famous Airport Managere

    https://www.bnuoj.com/v3/problem_show.php?pid=25653 [题意] 给定一个3*3的九宫格,模拟一个停机坪.第一个格子一定是'*',代表take off ...

  9. hdu 1027

    #include<stdio.h> #include<algorithm> using namespace std; int a[1100]; int main() {  in ...

  10. 前端开发:CSS3

    CSS介绍: CSS能够使页面具有美观一致的效果,并且能够让内容与格式分离,利于扩展 所以,CSS解决了下面两个问题: 1. 将HTML页面的内容与格式分离: 2. 提高web开发的工作效率. CSS ...