前言:

ES2015将Promise引入语言规范,包括fetch等在内的API也构建在Promise之上。作为让js摆脱“回调地狱”的重要一环和众多框架中的重要基础设施之一,学习如何自己实现一个Promise插件依然是前端工程师晋级之路上很有意义的练习之一。

Promise规范:

在众多Promise规范中,最著名的就是A+规范:

在此规范中,promise被定义为一个带有then方法的object或function,具有pending(可转换为fulfilled或rejected),fulfilled(不可转换,需带有一个value),rejected(不可转换,需带有一个reason)三种状态。

then方法调用时接受成功和失败的回调:promise.then(onFulfilled, onRejected)。onFulfilled在promise被fulfill时调用一次,并传入value;onRejected在promise被reject时调用一次,传入reason。一个promise的then可以被多次调用,因此调用then需要返回promise。

Promise可以被resolve,resolve的传入值也可以是一个promise(thenable的对象)。正确解析promise链,处理其中的resolve、exception、handler的返回值等等是关键所在。

具体文档可见:https://promisesaplus.com/

自己实现Promise:

mmDeferred(https://github.com/RubyLouvre/mmDeferred)是一个优秀的遵循Promise/A+规范的Deffered库,我们就来仿造它看看如何写一个Deffered库。

Deferred函数:

function Deferred() {

var state = "pending", dirty = false // 定义状态state,dirty

function ok(x) { // 定义转换state为fulfilled的接口,返回传入的x

state = "fulfilled"

return x

}

function ng(e) {

state = "rejected" // 定义转换state为rejected的接口,抛出异常

throw e

}

...

}

接下来我们添加Deferred的返回,它将是一个thenable的对象:

var dfd = {

callback: { // 定义resolve与reject的callback

resolve: ok,

reject: ng,

},

dirty: function() {

return dirty

},

state: function() {

return state

},

promise: {

then: function() { // 定义接口then,接受onFulfilled,onRejected作为参数

return _post.apply(null, arguments)

},

_next: null // 每个thenable通过_next完成promise链的构成

}

}

最后我们将给dfd对象绑定resolve与reject对象并返回:

"resolve,reject".replace(/\w+/g, function(method) {

dfd[method] = function() {

var that = this, args = arguments

// promise链上的第一个回调需要异步触发,但剩余链上的回调需要同步触发

if (that.dirty()) {

_fire.call(that, method, args)

} else {

setTimeout(function() {

_fire.call(that, method, args)

}, 0);

}

}

})

return dfd

这里用到了两个闭包内的函数_post与_fire,我们来看看他们各自做了什么:

function _post() {

var index = -1, fns = arguments;

"resolve,reject".replace(/\w+/g, function(method) {

var fn = fns[++index];

if (typeof fn === "function") {

// promise链的头结点,回调需要异步触发

dirty = true

// 修改dfd的resolve与reject函数,绑定then函数传入的回调

dfd.callback[method] = function() {

try {

var value = fn.apply(this, arguments)

state = "fulfilled"

return value

} catch (err) {

state = "rejected"

return err

}

}

}

})

// 形成promise链

var deferred = dfd.promise._next = Deferred(mixin)

return deferred.promise;

}

function _fire(method, array) {

var next = "resolve", value

// 根据状态触发回调

if (this.state() === "pending") {

var fn = this.callback[method]

try {

value = fn.apply(this, array);

} catch (e) {//处理notify的异常

value = e

}

if (this.state() === "rejected") {

next = "reject"

}

array = [value]

}

// promise链依次触发

var nextDeferred = this.promise._next

if (Deferred.isPromise(value)) {

value._next = nextDeferred

} else {

if (nextDeferred) {

_fire.call(nextDeferred, next, array);

}

}

}

还有isPromise 函数:

Deferred.isPromise = function(obj) {

return !!(obj && typeof obj.then === "function");

};

以上便是简化的Deferred库实现,在原库中还有一些优化,比如Deferred可以接受一个对象,以扩展这个对象为thenable,以及在resolve和reject之外还定义了notify(同时增加notify状态,可以继续转化为fulfilled或者rejected状态)以及ensure(无论状态如何转换都会触发),定义了Deferred.all与Deferred.any等等。

Promise基础的更多相关文章

  1. promise 基础知识

    promise 基础知识 proise:1.Promise是异步编程的一种解决方案,它有三种状态,分别是pending-进行中.resolved-已完成.rejected-已失败2.创建实例//met ...

  2. promise基础和进阶

    本文不对Promise的做过深的解析,只对基础的使用方法,然后会记录一些promise的使用技巧,可以巧妙的解决异步的常见问题. 在过去一直理解的是解决了一直异步回调的坑,但是用了npm async之 ...

  3. Promise 基础学习

    Promise 是ES6的特性之一,采用的是 Promise/A++ 规范,它抽象了异步处理的模式,是一个在JavaScript中实现异步执行的对象. 按照字面释意 Promise 具有"承 ...

  4. promise基础用法

    /** * Created by liyinghao on 2016/11/6. */ const fs = require('fs'); /* * 新建一个Promise对象,Promise就是一个 ...

  5. JavaScript promise基础示例

    const { info } = console // cooking function cook() { info('[COOKING] start cooking.') const p = new ...

  6. Promise编程基础

    (一) Promise基础     所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise是一个对象,从它可以获取异步操作的消 ...

  7. 前端基础进阶(十三):透彻掌握Promise的使用,读这篇就够了

    Promise的重要性我认为我没有必要多讲,概括起来说就是必须得掌握,而且还要掌握透彻.这篇文章的开头,主要跟大家分析一下,为什么会有Promise出现. 在实际的使用当中,有非常多的应用场景我们不能 ...

  8. ES6的promise对象应该这样用

    ES6修补了一位Js修真者诸多的遗憾. 曾几何时,我这个小白从js非阻塞特性的坑中爬出来,当我经历了一些回调丑陋的写法和优化的尝试之后,我深深觉得js对于多线程阻塞式的开发语言而言,可能有着其太明显的 ...

  9. 【JavaScript】JavaScript Promise 探微

    http://www.html-js.com/article/Promise-translation-JavaScript-Promise-devil-details 原文链接:JavaScript ...

随机推荐

  1. vim的复制粘贴小结(转)

    原文地址:http://lsong17.spaces.live.com/blog/cns!556C21919D77FB59!603.entry 内容: 用vim这么久 了,始终也不知道怎么在vim中使 ...

  2. li里元素都浮动 li 在IE6 7 下方会产生4px间隙问题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. Python3基础 list(reversed()) 将一个列表逆转并输出

    镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...

  4. base库插件---拖动

    /** * Created by Administrator on 2014/6/5 0005. Base-drag 基于Base库的拖拽插件 tags为你要拖拽的元素参数, 数组形式传入 */ $( ...

  5. JQuery中的mouseover和mouseenter的区别

    mouseover和mouseout是一对:mouseenter和mouseleave是一对. 相同点:都是鼠标经过就触发事件 不同点: 给外盒子一个经过触发事件,但是mouseover会在鼠标经过外 ...

  6. rsync+inotify实现数据的实时备份

    一.rsync概述 1.1.rsync的优点与不足 rsync与传统的cp.tar备份方式相比,rsync具有安全性高.备份迅速.支持增量备份等优点,通过rsync可以解决对实时性要求不高的数据备份需 ...

  7. 使用eclipse和maven一步一步配置web项目

    http://www.blogjava.net/kevonz/archive/2012/07/08/382542.html

  8. Js apply call方法详解

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  9. “canvas画布仿window系统自带画图软件"项目的思考

    "canvas画布仿window系统自带画图软件"项目的思考 首先贴上DEMO图,并没有美化效果.对UI有要求的,请自带补脑技术. 思考一 在做项目的过程中,我发现"工具 ...

  10. Extjs4新特性

    Extjs 4相对于之前的版本作出了重大的修正.其中包括全新的类系统.新平台的引入.API的修整和加强还有新组件的引入(如新的图表和图形组件).Extjs 4提供更快速.更稳定的用户体验,并且让开发人 ...