这次探究可能存在问题,为了不浪费您的时间,请忽略此文。

  先看的这篇有问题的文章

  ps: 2018年更新 这篇有问题的文章已经被作者团队删除了

  花了很长时间研究这篇文章,卡在实现串行Promise那儿了,一直看不明白。就在刚才,发现这篇文章是错的,在第一次用setTimeout( ,0)那儿就错了。虽然用setTimeout( ,0)实现了resolve的异步,但then那儿有个判断,给直接执行了,没用上这个异步。正确的代码应该是这样的

            function resolve(newValue) {
value = newValue;
// state = 'fulfilled';
setTimeout(function () {
state = 'fulfilled';
deferreds.forEach(function (deferred) {
deferred(value);
});
}, 0);
}

  气人,浪费了好多时间,重新找一篇文章研究吧。

  无语了,找了好多promise实现原理的文章,都是错的。先去看看微软的winjs库吧,不行的话,还是要看最初那篇有问题的文章。

  发现看不懂winjs的源码,3.0的源码竟然7万多行。那还是看最初的那篇文章吧,把错误的地方找出来改掉。不想花太多时间在这上面了,因为还有很多别的知识要看,只要下面的代码运行没问题就暂时放一下,更详细的探究以后会补上。

        var promise = new PromiseB(function  (resolve) {
resolve('aaaaa');
});
promise.then(function (value) {
console.log(value);
})
console.log('bbbbbb');

  只要能先输出'bbbbb'后输出'aaaaa'就当OK了。PromiseB是自己定义的一个Promise构造函数。好,下面开始大量使用那篇文章的内容。

  

为了让语言上更加准确和简练,本文做如下约定:

  • Promise:代表由 Promises/A+ 规范所定义的异步操作封装方式;
  • promise:代表一个 Promise 实例。

基础实现

为了增加代入感,本文从最为基础的一个应用实例开始探索:通过异步请求获取用户id,然后做一些处理。在平时大家都是习惯用回调或者事件来处理,下面我们看下 Promise 的处理方式:

// 例1

function getUserId() {
return new Promise(function (resolve) {
// 异步请求
Y.io('/userid', {
on: {
success: function (id, res) {
resolve(JSON.parse(res).id);
}
}
});
});
} getUserId().then(function (id) {
// do sth with id
});

getUserId 方法返回一个 promise,可以通过它的 then 方法注册在 promise 异步操作成功时执行的回调。自然、表意的 API,用起来十分顺手。

满足这样一种使用场景的 Promise 是如何构建的呢?其实并不复杂,下面给出最基础的实现:

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);
}

代码很短,逻辑也非常清晰:

  • 调用then方法,将想要在 Promise 异步操作成功时执行的回调放入 deferreds 队列;
  • 创建 Promise 实例时传入函数被赋予一个函数类型的参数,即 resolve,用以在合适的时机触发异步操作成功。真正执行的操作是将 deferreds 队列中的回调一一执行;
  • resolve 接收一个参数,即异步操作返回的结果,方便回调使用。

下面是我对上面代码的一些理解补充:

  先定义一个Promise构造函数,该构造函数有一个参数,该参数是一个函数。下面开始创造Promise实例,先将函数参数传入(注意该函数暂时未调用,是放到最后调用的),然后定义2个内部变量value和deferreds,接着定义实例的then方法(该方法在以后调用的时候,需要传入一个函数,该函数会放进deferreds队列里),接着定义resolve函数(该函数暂时不能被外部访问,而是通过fn以闭包方式调用的),该函数是用来执行deferreds队列里的代码的,最后执行fn函数,fn里就是开发者在实例化一个新promise对象时传入的函数。注意,现在是有问题的,resolve在实例构造的过程中和执行的时候,deferreds队列是空的,then的函数没有传进来,这个问题后面再解决。

  接着原文章的内容

  有时需要注册多个回调,如果能够支持 jQuery 那样的链式操作就好了!事实上,这很容易:

this.then = function (onFulfilled) {
deferreds.push(onFulfilled);
return this;
};

这个小改进带来的好处非常明显,当真是一个大收益的小创新呢:

// 例2

getUserId().then(function (id) {
// do sth with id
}).then(function (id) {
// do sth else with id
});

  注意,这样其实是有问题的,then返回的是原promise实例,这与promise 迷你书2.6节“专栏: 每次调用then都会返回一个新创建的promise对象”不相符,这个问题后面再谈。

  

延时

如果 promise 是同步代码,resolve 会先于 then 执行,这时 deferreds 队列还空无一物,更严重的是,后续注册的回调再也不会被执行了:

// 例3

function getUserId() {
return new Promise(function (resolve) {
resolve(9876);
});
} getUserId().then(function (id) {
// do sth with id
});

此外,Promises/A+ 规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。为解决这两个问题,可以通过 setTimeout 将 resolve 中执行回调的逻辑放置到 JS 任务队列末尾:

function resolve(value) {
setTimeout(function () {
deferreds.forEach(function (deferred) {
deferred(value);
});
}, 0);
}

引入状态

Hmm,好像存在一点问题:如果 Promise 异步操作已经成功,之后调用 then 注册的回调再也不会执行了,而这是不符合我们预期的。

解决这个问题,需要引入规范中所说的 States,即每个 Promise 存在三个互斥状态:pending、fulfilled、rejected。经过改进后的代码:

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;
setTimeout(function () {
state = 'fulfilled';
deferreds.forEach(function (deferred) {
deferred(value);
});
}, 0);
} fn(resolve);
}

我把原文state = 'fulfilled'改了位置。

resolve 执行时,会将状态设置为 fulfilled,在此之后调用 then 添加的新回调,都会立即执行。

似乎少了点什么,哦,是的,没有任何地方将 state 设为 rejected,这个问题稍后会聊,方便聚焦在核心代码上。

串行 Promise

在这一小节,将要探索的是 Promise 的 Killer Feature:串行 Promise,这是最为有趣也最为神秘的一个功能。

串行 Promise 是指在当前 promise 达到 fulfilled 状态后,即开始进行下一个 promise(后邻 promise)。例如获取用户 id 后,再根据用户 id 获取用户手机号等其他信息,这样的场景比比皆是:

// 例4

getUserId()
.then(getUserMobileById)
.then(function (mobile) {
// do sth with mobile
}); function getUserMobileById(id) {
return new Promise(function (resolve) {
Y.io('/usermobile/' + id, {
on: {
success: function (i, o) {
resolve(JSON.parse(o).mobile);
}
}
});
});
}

这个 feature 实现的难点在于:如何衔接当前 promise 和后邻 promise。

首先对 then 方法进行改造:

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);
}

  先停一下,ES6中的链式调用是怎么传参的呢?看一下迷你书的2.4.2. “promise chain 中如何传递参数”,就是在then方法的函数参数里return一下就可以了,我们按照这个接着研究promise的原理。

  接下来的不怎么好解释了,直接看我写的代码,代码里包含了对resolve的改造,总体地看,多看几遍思路就清楚了。

        function PromiseB(fn) {
var state = 'pending',
value = null,
deferreds = []; this.then = function (onFulfilled) {
return new PromiseB(function (resolve2) {
handle({
onFulfilled: onFulfilled || null,
resolve: resolve2
});
});
}; 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 () {
state = 'fulfilled';
deferreds.forEach(function (deferred) {
handle(deferred);
});
}, 0);
} fn(resolve);
} var promise = new PromiseB(function (resolve) {
resolve('aaaaa');
});
promise.then(function (value) {
console.log(value);
return 'ccccc'
}).then(function (value) {
console.log(value);
})
console.log('bbbbbb');

  脑子晕掉了,我已经暂时不想看到Promise这东西,暂时搁置。

promise实现原理的更多相关文章

  1. promise的原理

    promise的原理 一旦状态改变,就不会再变,任何时候都可以得到这个结果.Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 re ...

  2. Promise核心原理解析

    作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...

  3. JavaScript之Promise实现原理(手写简易版本 MPromise)

    手写 Promise 实现 Promise的基本使用 Promise定义及用法详情文档:Promise MAD文档 function testPromise(param) { return new P ...

  4. 扒一扒PROMISE的原理,大家不要怕!

    在前端的日常工作中,回调函数(callback)应该是见怪不怪了,但是当回调函数遇上了异步(async),这就令人发指了.那么异步是什么意思呢,简单地说就是不等你执行完,就先执行下方的代码了. 举个

  5. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  6. Promise 原理

    异步:可同时好几件事,互不影响: 同步:按循序一件一件.... 异步好多缺点:.... promise就是解决异步计算的这些缺点的,主要用于: 1.异步计算: 2.可以将异步操作队列化  按期望的顺序 ...

  7. Promise原理剖析

    传统的异步回调编程最大的缺陷是:回调地狱,由于业务逻辑非常复杂,代码串行请求好几层:并行请求以前也要通过引用step.async库实现.现在ES6推出了Promise,通过Promise的链式调用可以 ...

  8. 这一次,彻底弄懂 Promise 原理

    作者声明 本人将迁移至个人公众号「前端Q」及「掘金」平台写文章.博客园的文章将不再及时更新发布.欢迎大家关注公众号「前端Q」及我的掘金主页:https://juejin.im/user/5874526 ...

  9. ES6之promise原理

    我在这里介绍了promise的原理: https://juejin.im/post/5cc54877f265da03b8585902 我在这里 仅仅张贴 我自己实现的简易promise——DiProm ...

随机推荐

  1. 关于淘宝店铺装修弹出层popup的记录

    小龙最近做了一下下淘宝的店铺装修,里面封装的widget深不见底,刚刚整明白popup,也就是弹出层的使用方法,大神勿喷: <div class="area001">触 ...

  2. LINUX 根目录说明

    linux目录:/bin      bin是Binary的缩写.这个目录存放着最经常使用的命令./boot 这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件./data / ...

  3. 【BZOJ3172】[Tjoi2013]单词 AC自动机

    [BZOJ3172][Tjoi2013]单词 Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input ...

  4. [DataMining]WEEK1 - text-retrieval and search engine

    What does a computer have to do in order to understand a natural language sentence? What is ambiguit ...

  5. 伪类before和after

     以你添加的元素为基础!在他的里面!也就是他的内容的前面或者后面添加东西!  如果原来的元素没有内容会出现什么情况?(伪类的宽和高和元素的相等)

  6. var a=b=c=1; 和 var a=1, b=2, c=3; 的区别。

    function test(){ var a=b=c=1; var a=1, b=2,c=3; } 1中b\c 为全局变量, a为私量 2中a\b\c为私量

  7. Project 'king.commons' is missing required library: 'lib/plweb.jar' Build path Build Path Problem

    问题描述:之前在项目里引用一个jar 包,后来不用了删掉 ,但是没有删干净,然后报以下错误. 解决方案: 1.删除libraries 2.找到该项目下的 .classpath 文件,用记事本打开 ,删 ...

  8. DoD and DoR

    Definition of Ready User Story is defined Acceptance criteria(functional and non-functional requirem ...

  9. cf 红名计划!

    我要成为红名爷! 这是现在好弱好弱的窝 >_< ****************UPD ON 2015/12/10 0:20 啊啊啊啊啊啊啊啊啊啊啊啊把时间记错了啊QAQ 我也不知道为什么 ...

  10. 谈 CSS 模块化

    以前看过模块化的相关资料以及解释,对模块化有了一个表皮的了解,自己也做了一些相关的实践,由于接触到的项目交小,所以也没能更好的去体现和理解模块化,但总体还是有那么一些感悟,但是如果要说怎么才能算是好的 ...