博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何实现一个符合promiseA+规范的promise
阅读量:6433 次
发布时间:2019-06-23

本文共 10201 字,大约阅读时间需要 34 分钟。

前言

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

编写符合promiseA+规范的promise实现

在实现之前,可以先看一下

1. 创建promise构造函数


这里先实现promise最基本的功能:promise创建后立即执行;在then时执行相应的函数;捕获错误立即变成reject态

// promise里只有一个参数,叫executor(执行器)function Promise(executor) {    let self = this;    self.status = 'pending';//等待态    self.value = undefined;//默认成功的值    self.err = undefined;//默认失败的值    function resolve(value) {        if (self.status === 'pending') {            self.status = 'resolved';            self.value = value;        }    }    function reject(err) {        if (self.status === 'pending') {            self.status = 'rejected';            self.err = err;        }    }    try {//捕获时发生异常,直接变成reject态,抛出错误        executor(resolve, reject);//promise实例创建后,立即执行    } catch (error) {        reject(error);      }}//在prototype上定义then实例方法Promise.prototype.then = function (onFulfilled, onRejected) {    let self = this;    if (self.status === 'resolved') {        onFulfilled(self.value);    }    if (self.status === 'rejected') {        onRejected(self.err);    }}复制代码

这里我们先测试一下我们的Promise

这里便实现了基本功能,前面说过Promise 是异步编程的一种解决方案; 我们加个异步逻辑运行一下:

2. Promise异步调用


我们都知道异步代码并不会立即执行,这时既不是resolved也不是rejected,而是pending

在之前的状态判断里面,正好丢了一个pending状态。

OK,这时需要在then里判断当status为pending时,先将onFulfilled, onRejected存入数组里,当status改变时,再遍历数组让里面的函数依次执行,看代码。

(1)申明两个存放onFulfiled(),onRejected()的数组

function Promise(resolver) {    let self = this;    self.status = 'pending';//等待态    self.value = undefined;//默认成功的值    self.err = undefined;//默认失败的值    self.onResolvedCallbacks = []; // 存放then成功的回调    self.onRejectedCallbacks = []; // 存放then失败的回调    function resolve(value) {        if (self.status === 'pending') {            self.status = 'resolved';            self.value = value;            self.onResolvedCallbacks.forEach(fn=>{//调用resolve时,依次执行数组里的函数                fn();            })        }    }    function reject(err) {        if (self.status === 'pending') {            self.status = 'rejected';            self.err = err;            self.onRejectedCallbacks.forEach(fn=>{                fn();            })        }    }    try {//捕获时发生异常,直接抛出错误        resolver(resolve, reject);//promise实例创建后,立即执行它的方法    } catch (error) {        reject(error)    }}复制代码

(2)接着在then方法里添加pending的判断

Promise.prototype.then = function (onFulfilled, onRejected) {    let self = this;    if (self.status === 'resolved') {        onFulfilled(self.value);    }    if (self.status === 'rejected') {        onRejected(self.err);    }    if(self.status==='pending'){// 此时没resolved,也没rejectd        self.onResolvedCallbacks.push(()=>{            onFulfilled(self.value);        });        self.onRejectedCallbacks.push(()=>{            onRejected(self.err);        })    }}复制代码

再看刚刚的异步逻辑

1s后就执行成功了,是不是很神奇,再看下面:

3. Promise链式调用


(1)规范里说在同一个promise里then可以被多次调用。

(2)jquery能实现链式调用靠的是返回this,而promise不能返回this,规范里又说它返回的是一个新的Promise实例 (注意,不是原来那个Promise实例)

在then里新建一个promise2并为每一个状态包一个Promise

这里就返回了一个新的promise2,在promise2里也需要调用resolve或者reject;这里申明一个 x 来储存上一次then的返回值 规范里说只要上一次then有返回值,下一次then一定调用成功态resolve(x) 再来看看规范,规范里说道

(1)x可能是一个promise;

(2)可能是一个对象或者方法;

(3)也有可能是一个普通的值

这时需要一个方法来处理x

3.1 对onFulfilled和onRejected的返回值进行处理


于是引入一个处理方法resolvePromise(promise2, x, resolve, reject); 这里四个参数分别是

  1. Promise2是我们返回的新的promise
  2. x是上一次then的返回值
  3. resolve是成功的方法
  4. reject是失败的方法

这里需要注意一下,有些人写的promise可能会既调用成功,又调用失败,如果两个都调用先调用谁另一个就忽略掉。 在resolvePromise(promise2, x, resolve, reject)里增加一个判断called表示是否调用过成功或者失败,看代码:

function resolvePromise(promise2, x, resolve, reject) {    if (promise2 === x) {//promise2和x不能相同        return reject(new TypeError('循环引用了'))    }    let called;// 表示是否调用过成功或者失败    //这里对x的类型进行判断    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {        try {  // 判断x是不是promise,如果x是对象并且x的then方法是函数我们就认为他是一个promise            let then = x.then;            if (typeof then === 'function') {                then.call(x, function (y) {                    if (called) return                    called = true                    // y可能还是一个promise,在去解析直到返回的是一个普通值                    resolvePromise(promise2, y, resolve, reject)                }, function (err) { //失败                    if (called) return                    called = true                    reject(err);                })            } else {                resolve(x)            }        } catch (e) {            if (called) return            called = true;            reject(e);        }    } else { // 说明是一个普通值1        resolve(x); // 表示成功了    }}复制代码

相应的将前面的代码进行一些更改

这时我们就实现了promise的链式调用。

4. 值的穿透问题


如果在then中什么都不传,值会穿透到最后调用的时候;

这时需要在then里给onFulfilled和onRejected写一个默认的函数

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {        return value;    }    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {        throw err;//这里需要抛出错误,不能return err,否则会在下一次调用成功态    }复制代码

5. then的异步实现


规范里要求,所有的onFulfilled和onRejected都要确保异步执行

这里以resolve为例,写一个setTimeout():

6. defer语法糖


在使用promise的过程中,我们都需要先new Promise(),比如说:

function read() {    let fs = require('fs');    let promise = new Promise(function(resolve,reject){        fs.readFile('./1.txt','utf8',function(err,data){            if(err) reject(err);            resolve(data);        })    });    return promise}复制代码

在Promise中,它为我们提供了一个语法糖Promise.defer,用Promise.defer只需这样写:

function read() {    let defer = Promise.defer()    require('fs').readFile('.//1.txt', 'utf8', function (err, data) {        if(err) defer.reject(err);        defer.resolve(data);    })    return defer.promise;}复制代码

为此,再为我们的Promise加一个defer方法:

Promise.defer = Promise.deferred = function () {    let dfd = {};    dfd.promise = new Promise(function (resolve, reject) {        dfd.resolve = resolve;        dfd.reject = reject;    });    return dfd}复制代码

在这里,我们基本实现了一个比较完整的promise;当然Promise还有许多静态方法,还有js的异步发展史,这些可以在下一次进行讨论。 完整代码:

// promise里只有一个参数,叫executor(执行器)function Promise(executor) {    let self = this;    self.status = 'pending';//等待态    self.value = undefined;//默认成功的值    self.err = undefined;//默认失败的值    self.onResolvedCallbacks = []; // 存放then成功的回调    self.onRejectedCallbacks = []; // 存放then失败的回调    function resolve(value) {        if (self.status === 'pending') {            self.status = 'resolved';            self.value = value;            self.onResolvedCallbacks.forEach(function (fn) {                fn();            });        }    }    function reject(err) {        if (self.status === 'pending') {            self.status = 'rejected';            self.err = err;            self.onRejectedCallbacks.forEach(function (fn) {                fn();            });        }    }    try {//捕获时发生异常,直接变成reject态,抛出错误        executor(resolve, reject);//promise实例创建后,立即执行    } catch (error) {        reject(error);      }}function resolvePromise(promise2, x, resolve, reject) {    if (promise2 === x) {//promise2和x不能相同        return reject(new TypeError('循环引用了'))    }    let called;// 表示是否调用过成功或者失败    //这里对x的类型进行判断    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {        try {  // 判断x是不是promise,如果x是对象并且x的then方法是函数我们就认为他是一个promise            let then = x.then;            if (typeof then === 'function') {                then.call(x, function (y) {                    if (called) return                    called = true                    // y可能还是一个promise,在去解析直到返回的是一个普通值                    resolvePromise(promise2, y, resolve, reject)                }, function (err) { //失败                    if (called) return                    called = true                    reject(err);                })            } else {                resolve(x)            }        } catch (e) {            if (called) return            called = true;            reject(e);        }    } else { // 说明是一个普通值1        resolve(x); // 表示成功了    }}//在prototype上定义then实例方法Promise.prototype.then = function (onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {        return value;    }    onRejected = typeof onRejected === 'function' ? onRejected : function (err) {        throw err;//这里需要抛出错误,不能return err,否则会在下一次调用成功态    }    let self = this;    let promise2; //返回的promise    if (self.status === 'resolved') {        promise2 = new Promise(function (resolve, reject) {            setTimeout(function () {                try {                    let x = onFulfilled(self.value);                    resolvePromise(promise2, x, resolve, reject);                } catch (e) {                    reject(e);                }            })        })    }    if (self.status === 'rejected') {        promise2 = new Promise(function (resolve, reject) {            setTimeout(function () {                try {                    let x = onRejected(self.err);                    resolvePromise(promise2, x, resolve, reject);                } catch (e) {                    reject(e);                }            })        })    }    // 当调用then时可能没成功 也没失败    if (self.status === 'pending') {        promise2 = new Promise(function (resolve, reject) {            // 此时没有resolve 也没有reject            self.onResolvedCallbacks.push(function () {                setTimeout(function () {                    try {                        let x = onFulfilled(self.value);                        resolvePromise(promise2, x, resolve, reject);                    } catch (e) {                        reject(e)                    }                })            });            self.onRejectedCallbacks.push(function () {                setTimeout(function () {                    try {                        let x = onRejected(self.err);                        resolvePromise(promise2, x, resolve, reject);                    } catch (e) {                        reject(e);                    }                })            });        })    }    return promise2;}Promise.defer = Promise.deferred = function () {    let dfd = {};    dfd.promise = new Promise(function (resolve, reject) {        dfd.resolve = resolve;        dfd.reject = reject;    });    return dfd}module.exports = Promise;复制代码

7.Promise测试


npm i -g promises-aplus-testspromises-aplus-tests Promise.js复制代码

转载地址:http://zhxga.baihongyu.com/

你可能感兴趣的文章
orocos_kdl学习(一):坐标系变换
查看>>
两步完成利用procdump64+mimikatz获取win用户密码
查看>>
Mac 的命令行配置字体颜色
查看>>
linux后台执行程序
查看>>
剑指offer---二叉搜索树的后序遍历序列
查看>>
Bit Operation妙解算法题
查看>>
VLC Play in web
查看>>
详解PNG文件结构
查看>>
Statistics与Machine Learning有什么区别
查看>>
python 记录
查看>>
Silverlight 鼠标双击 事件
查看>>
Actionscript通过构造自定义事件和方法,谈谈可选参数的问题
查看>>
ssm+maven+pageHelper搭建maven项目实现快速分页
查看>>
Android系统剪切板
查看>>
Android后台服务拍照的解决方式
查看>>
SQL Server索引
查看>>
VC UTF8转ANSI
查看>>
企业应用开发模式 ERP项目中应用到的技术和工具
查看>>
Java:多线程,Exchanger同步器
查看>>
计算字符串和文件的MD5值
查看>>