4
文 / 景朝霞
来源公号 / 朝霞的光影笔记
ID / zhaoxiajingjing
❥❥❥❥点个赞,让我知道你来过~❥❥❥❥

Promise的好处

Promise 可以解决的问题

  1. 把你从回调地狱中解救出来
  2. 让你优雅的捕获错误
  3. 为你分担异步并发的难题
// 此处使用node举例,不会不要紧,先混个脸熟。再见就不陌生了呀
let fs = require('fs');
// 异步读取文件
fs.readFile('./name', function (err, data){
    if(err){}
    fs.readFile(data, function (err, address){
        if(err){}
        fs.readFile(address, function (err, product){
            // 1)深陷在回调地域中不能抽身
            if(err){
                // 2)捕获错误。OMG,我哪里错了?!?!告诉我,肯定改~
            }
        });
    });
});

// 3) 你的名字、你的地址。都告诉我,惊喜才会送到你面前呀~
fs.readFile('./name', function (err, data){});
fs.readFile('./address', function (err, data){});

Promise 使用的例子

状态变化

  • Promise 有3种状态
  • pending 等待态
  • fulfilled 成功态
  • rejected 失败态

【等待态 -> 成功态】 or 【等待态 -> 失败态】二选一,你来定。

图片描述

  • Promise 是个类,接收一个函数参数 executor 执行器,一上来就执行了。这里是同步的。
  • 每一个Promise的实例上都有一个then方法。是基于回调实现的。
console.log('一封情书');

let p = new Promise((resolve, reject)=>{
    console.log('executor请说出你的选择:');
    resolve('你中意我~(*^▽^*)');
    reject('你发了好人卡(╥﹏╥)o');
});

p.then((value)=>{
    console.log('成功态', value);
}, (reason) => {
    console.log('失败态', reason);
});

console.log('纸短情长');

图片描述

链式调用

console.log('-----一封情书-----');

let p = new Promise((resolve, reject) => {
    console.log('executor 请说出你的选择:');
    resolve('你中意我~(*^▽^*)');
    reject('你发了好人卡(╥﹏╥)o');
});

p.then((value) => {
    console.log('成功态---', value);
}, (reason) => {
    console.log('失败态---', reason);
}).then((value) => {
    console.log('---爱你一万年~(*^▽^*)');
}, (reason) => { 
    console.log('---伤心总是难免的o(╥﹏╥)o');
});
console.log('~~~纸短情长~~~');

图片描述

异步请求

  • name.txt
zhaoxiajingjing
  • 3.js
let fs = require('fs');
let p = new Promise((resolve, reject)=>{
    fs.readFile('./name.txt', 'utf8', function (err, data){
        if(err){
            return reject(err);
        }
        resolve(data);
    });
});
p.then((value)=>{
    console.log('成功了', value);
}, (reason)=>{
    console.log('失败了', reason);
});

图片描述

复杂的使用 Promise

下面的例子输出什么呢?

let fs = require('fs');

function read(filePath) {
    return new Promise((resolve, reject) => {
        fs.readFile(filePath, 'utf8',function(err, data){
            if(err) {
                return reject(err);
            }
            resolve(data);
        });
    });
}

read('./name.txt')
// then-1
.then(function (data) {
    console.log('data①', data);
    return new Promise((resolve, reject) => {
        reject('错误了');
    });
})
// then-2
.then((data) => {
    console.log('data②', data);
}, err => {
    console.log('err②', err);
})
// then-3
.then((data) => {
    console.log('data③', data);
}, (err) => {
    console.log('err③', err);
});

图片描述

OK,提炼一下重点:

  1. 有三个状态。怎么变化的?
  2. executor。怎么执行的?
  3. then方法,成功态和失败态的回调。
  4. then方法的链式调用。
  5. Promise 处理异步。

实现以上内容

Promise 基本实现

  • new Promise 会传一个函数作为参数。这个函数有两个参数:resolve 成功 reject 失败,都是用于改变状态的。都在实例上,一个 Promise 一生只改变一次状态
  • 每个实例上都有一个 then 方法,异步的。对于成功态和失败态的回调:onFulfilled onRejected

代码1,传送门~

const PENDING = 'pending';
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';

class Promise{
    constructor(execuotr){
        const status = PENDING; // 我等着你给答案~
        this.value;
        this.reason;

        let resolve = (value)=>{
            if(status === PENDING) {
                this.status = SUCCESS; // 你中意我~
                this.value = value;
            }
        };

        let reject = (reason)=>{
            if(status === PENDING) {
                this.status = FAIL; // 你发了好人卡
                this.reason = reason;
            }
        };

        // 是同步的哦~
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected){
        if(this.status === SUCCESS) {
            onFulfilled(this.value);  // 爱你一万年~
        }
        if(this.status === FAIL) {
            onRejected(this.reason); // 伤心总是难免的
        }
    }
}

Pomise 解决异步问题

Promise 是个容器,里面可以放一些异步的请求,请求成功了走成功态,请求失败了走失败态。当然,你要反过来走也可以哒~

代码2,传送门~

const PENDING = 'pending';
const SUCCESS = 'fulfilled';
const FAIL = 'rejected';

class Promise {
    constructor(executor){
        this.status = PENDING;
        this.value;
        this.reason;

        // 用来存储 订阅的内容的
        this.onSuccessCallbacks = [];
        this.onFailCallbacks = [];

        let resolve = (value)=>{
            if(this.status === PENDING) {
                this.status = SUCCESS;
                this.value = value;
                this.onSuccessCallbacks.forEach(fn => fn());
            }
        };
        let reject = (reason)=>{
            if(this.status === PENDING) {
                this.status = FAIL;
                this.reason = reason;
                this.onFailCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected){
        if(this.status === SUCCESS){
            onFulfilled(this.value);
        }
        if(this.status === FAIL){
            onRejected(this.reason);
        }
        // 当Promise里面有异步请求控制状态改变时,会先走到then方法里面
        if(this.status === PENDING) {
            this.onSuccessCallbacks.push(()=>{
                onFulfilled(this.value);
            });
            this.onFailCallbacks.push(()=>{
                onRejected(this.reason);
            });
        }
    }
}

Promise 里面有异步请求时候,会先走到 then方法里面了。此时,需要把成功态回调和失败态回调先存储起来,等到异步请求回来以后变更了状态,再触发执行。

Promise 的链式

then 方法 返回一个新的 Promise

Promise 一生只能改变一次状态。那么,Promise 的链式调用then方法,说明每次都会返回一个新的Promise

代码3,传送门~

const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';

class Promise {
    constructor(executor) {
       // ... executor 代码
    }
    then(onFulfilled, onRejected) {
        let promise2;

        promise2 = new Promise((resolve, reject)=>{
            if (this.status === SUCCESS) {
                try { // 用 try catch 捕获同步的报错
                    // 成功态的回调的返回值 x
                    // 【问题】 如果 x 是一个promise,那么需要取得x执行后的结果
                    let x = onFulfilled(this.value);
                    resolve(x);

                } catch (e) {
                    reject(e);
                }
            }
            if (this.status === FAIL) {
                try {
                    let x = onRejected(this.reason);
                    resolve(x);
                } catch (e) {
                    reject(e);
                }
            }
            if (this.status === PENDING) {
                this.onSuccessCallbacks.push(()=>{
                    try {
                        let x = onFulfilled(this.value);
                        resolve(x);
                    } catch (e) {
                        reject(e);
                    }
                });
                this.onFailCallbacks.push(()=>{
                    try {
                        let x = onRejected(this.reason);
                        resolve(x);
                    } catch (e) {
                        reject(e);
                    }
                });
            }
        });

        return promise2;
    }
}

解析 x

如何判断 x 是Promise,还是一个普通值?【参考规范 https://promisesaplus.com "Promise A+" 2.2.7】

代码4,传送门~

const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';

function resolvePromise(promise2, x, resolve, reject) {
    // 死循环了,容错
    if(promise2 === x) {
        return reject('TypeError: Chaining cycle detected for promise~~~~');
    }
    // 判断 x 类型
    if(typeof x === 'function' || (typeof x === 'object' && x != null)) {
        // 这个才有可能是 promise
        try {
            let then = x.then;
            if(typeof then === 'function') {
                // 此时,认为就是个promise
                // 如果promise是成功的,那么结果向下传递,如果失败了就传到下一个失败里面去
                then.call(x, y=>{
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch (e) {
            reject(e);
        }
    } else {
        // x 肯定不是一个promise
        resolve(x);
    }
}

class Promise {
    constructor(executor) {
        // ... executor 代码
    }
    then(onFulfilled, onRejected) {
        let promise2;

        promise2 = new Promise((resolve, reject) => {
            if (this.status === SUCCESS) {
                // 用定时器模拟此时promise2已经能获取到了
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            }
            // 其他情况同理,先以一个为例说明
        });
        
        return promise2;
    }
}

严谨度

Promise 给你的承诺,一句情话:一个 Promise 一生只改变一次状态

代码5,传送门~

const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';

function resolvePromise(promise2, x, resolve, reject) {
    // 死循环了,容错
    if(promise2 === x) {
        return reject('TypeError: Chaining cycle detected for promise~~~~');
    }
    let called;
    if(typeof x === 'function' || (typeof x === 'object' && x != null)) {
        try {
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x, y=>{
                    if(called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if(called) return;
                    called = true;
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch (e) {
            if(called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x);
    }
}

class Promise {
    constructor(executor) {
        // .... executor 的代码
    }
    then(onFulfilled, onRejected) {
        let promise2;

        promise2 = new Promise((resolve, reject) => {
            if (this.status === SUCCESS) {
                // 用定时器模拟此时promise2已经能获取到了
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            }
            // 其他情况同理,先以一个为例说明
        });

        return promise2;
    }
}

值的穿透

let p = new Promise((resolve, reject)=>{
    resolve(1000);
});
p.then().then().then(data => {
    console.log(data);
});

值可以传过去

const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';

function resolvePromise(promise2, x, resolve, reject) {
    // ... 判断 x 的值
}

class Promise {
    constructor(executor) {
        // ... executor 代码
    }
    then(onFulfilled, onRejected) {
        // 值穿透
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err};
        // ... promise2 的判断
    }
}

测试

  • 测试这个库是否符合我们的promise A+ 规范
  • promises-aplus-tests 用来测试当前的库是否符合规范
  • npm i promises-aplus-tests -g
  • promises-aplus-tests 文件名

可测试代码,传送门~

Promise A+ 规范

Promise 是一个构造函数,是个类。默认高版本浏览器,node都自带了。不用考虑兼容性,放心大胆的使用吧!如果真不兼容,那就用es6-promise包自己是一套吧~

Promise A+

https://promisesaplus.com

容错

上面的内容,还需要一部分容错。就是当executor 里面的有一个promise的时候,执行的结果。

let Promise = require('./promise.js');
let p = new Promise((resolve, reject)=>{
    resolve(new Promise((resolve, reject)=>{
        reject(404);
    }));
});
p.then(value => console.log(1, value), reason => console.log(2, reason));

// 输出:
// 2 404

在同步执行时,resolve 的value是一个Promise,那么需要等它的结果。

const SUCCESS = 'fulfilled';
const FAIL = 'rejected';
const PENDING = 'pending';

function resolvePromise(promise2, x, resolve, reject) {
    // ... 校验x
}

class Promise {
    constructor(executor) {
        // ... code

        let resolve = (value) => {
            if (value instanceof Promise) {
                return value.then(resolve, reject);
            }

            if (this.status === PENDING) {
                this.status = SUCCESS;
                this.value = value;
                this.onSuccessCallbacks.forEach(fn => fn());
            }
        };
        // ... code
    }
    then(onFulfilled, onRejected) {
        // ... then 方法
    }
}

module.exports = Promise;

交流

公号首发:朝霞的光影笔记
在这里,一起成长~


Pink
269 声望14 粉丝

没有什么是20遍解决不了的,如果有~那就再写20遍