js异步发展历史与Promise原理分析

关于异步

所谓"异步",简单说就是一个任务不是连续完成的,能够理解成该任务被人为分红两段,先执行第一段,而后转而执行其余任务,等作好了准备,再回过头执行第二段。前端

好比,有一个任务是读取文件进行处理,任务的第一段是向操做系统发出请求,要求读取文件。而后,程序执行其余任务,等到操做系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫作异步。ajax

相应地,连续的执行就叫作同步。因为是连续执行,不能插入其余任务,因此操做系统从硬盘读取文件的这段时间,程序只能干等着。后端

简单的说同步就是你们排队工做,异步就是你们同时工做。若是你还不太明白异步与同步,多看看JS基础的文章。api

异步的发展历史

1.CallBack写法

CallBack意为“回调函数”,即异步操做执行完后触发执行的函数,例如:promise

$.get("http://api.xxxx.com/xxx",callback);

当请求完成时就会触发callback函数。框架

callback能够完成异步操做,可是经历过JQuery时代的人应该都对某一种需求折磨过,举个例子:项目要求前端ajax请求后端接口列表类型名称,而后在用类型名称ajax请求列表id,在用id请求列表具体内容,最后代码大概是这样的异步

$.ajax({
    url: "type",
    data:1,
    success: function (a) {
        $.ajax({
            url: "list",
            data:a,
            success: function (b) {
                $.ajax({
                    url: "content",
                    data:b,
                    success: function (c) {
                        console.log(c)
                    }
                })
            }
        })
    }
})

这是是单纯的嵌套代码,如若再加上业务代码,代码可读性可想而知,若是是开发起来还好,可是后期的维护和修改的难度足以让人疯掉。这就是那个JQuery时代的“回调地狱”问题。async

2.Promise

为了解决“回调地狱”问题,提出了Promise对象,而且后来加入了ES6标准,Promise对象简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。函数

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操做成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用本身部署。ui

resolve函数的做用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操做成功时调用,并将异步操做的结果,做为参数传递出去;reject函数的做用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操做失败时调用,并将异步操做报出的错误,做为参数传递出去。

Promise实例生成之后,能够用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then方法能够接受两个回调函数做为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不必定要提供。这两个函数都接受Promise对象传出的值做为参数。

这样采用 Promise,解决“回调地狱”问题,好比连续读取多个文件,写法以下。

var readFile = require('fs-readfile-promise');

readFile(fileA)
.then(function (data) {
  console.log(data.toString());
})
.then(function () {
  return readFile(fileB);
})
.then(function (data) {
  console.log(data.toString());
})
.catch(function (err) {
  console.log(err);
});

可见这种写法要比CallBack写法直观的多。可是,有没有更好的写法呢?

3.Generator 函数

Genrator 函数要用* 来比标识,yield关键字表示暂停。将函数分割出好多个部分,调用一次next就会继续向下执行。返回结果是一个迭代器,迭代器有一个next方法。

function* read() {
    console.log(1);
    let a = yield '123';
    console.log(a);
    let b = yield 9
    console.log(b);
    return b;
}
let it = read();
console.log(it.next('213')); // {value:'123',done:false}
console.log(it.next('100')); // {value:9,done:false}
console.log(it.next('200')); // {value:200,done:true}
console.log(it.next('200')); // {value:200,done:true}

yield后面跟着的是value的值,yield等号前面的是咱们当前调用next传进来的值,而且第一次next传值是无效的。

处理异步的时候Generator和Promise搭配使用

let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);//将readFile转为Promise对象的实例
function* r() {
    let content1 = yield read('./2.promise/1.txt', 'utf8');
    let content2 = yield read(content1, 'utf8');
    return content2;
}

这样看起来是咱们想要的样子,可是只写成这样还不行,想获得r()的结果还要对函数进行处理

function co(it) {
    return new Promise(function (resolve, reject) {
        function next(d) {
            let { value, done } = it.next(d);
            if (!done) {
                value.then(function (data) { // 2,txt
                    next(data)
                }, reject)
            } else {
                resolve(value);
            }
        }
        next();
    });
}
co(r()).then(function (data) {
    console.log(data)//获得r()的执行结果
})

这样的处理方式显然很麻烦,并非咱们想要,咱们想要直观的写起来就就像同步函数,并且简便的方式处理异步。有这样的方法吗?

4.async-await函数

ES2017 标准引入了 async 函数,使得异步操做变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);

async function r(){
    try{
        let content1 = await read('./2.promise/100.txt','utf8');
        let content2 = await read(content1,'utf8');
        return content2;
    }catch(e){ // 若是出错会catch
        console.log('err',e)
    }
}

一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

async函数返回的是promise

r().then(function(data){
    console.log(data);
},function(err){

})

至此,async-await函数已经能够咱们满意,之后会不会出现更优秀的方案?以咱们广大程序群体的创造力,相信必定会有的。

Promise原理分析

async-await函数其实只是Generator函数的语法糖,而Generator函数的实现方式也是要基于Promise,因此咱们队Promise的实现原理进行分析。

Promise对象有如下几种状态:

  • pending: 初始状态, 既不是 fulfilled 也不是 rejected.
  • fulfilled: 成功的操做.
  • rejected: 失败的操做.

在上面了解了Promise的基本用法后,咱们先将Promise的框架搭起来

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = undefined; // 默认失败的缘由
    self.onResolvedCallbacks = []; // 存放then成功的回调
    self.onRejectedCallbacks = []; // 存放then失败的回调
    function resolve(value) { // 成功状态
        
    }
    function reject(reason) { // 失败状态
        
    }
    try {
        executor(resolve, reject)
    } catch (e) { // 捕获的时候发生异常,就直接失败了
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRjected) {
//then方法
})

接下来当调用成功状态resolve的时候,会改变状态,执行回调函数:

function resolve(value) { // 成功状态
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }

reject函数同理

function reject(reason) { // 失败状态
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }

接下来咱们完成then函数

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            
        })
    }
    // 当调用then时可能没成功 也没失败
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {

        })
    }
    return promise2;
}

Promise容许链式调用,因此要返回一个新的Promise对象promise2

Promise.prototype.then = function (onFulfilled, onRjected) {
    //成功和失败默认不穿给一个函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw 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);
                    // x多是别人promise,写一个方法统一处理
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRjected(self.reason);
                    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 = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}

在promise2内部定义一个变量x为回调函数的返回值,因为返回值可能会有多种可能的状况,因此咱们定义一个resolvePromise函数统一处理

返回值能够分为

  • promise返回本身 (报错循环引用)
  • 返回promise对象 (根据promise对象调用成功或失败回调函数)
  • 返回普通值 (调用成功回调函数传入返回值)
  • 报错 (调用失败回到传入错误)
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) { 
        return reject(new TypeError('循环引用了'))
    }
    // 断定x是否是一个promise,promise应该是一个对象
    let called; // 表示是否调用过成功或者失败
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try { // {then:1}
            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 { // 说明是一个普通值
        resolve(x); // 调用成功回调
    }
}

若是返回值为对象或函数,且有then方法,那咱们就认为是一个promise对象,去调用这个promise进行递归,直到返回普通值调用成功回调。

最后,再加上一个catch方法,很简单

Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}

这些就是promise的主要功能的原理,附上完整代码

function Promise(executor) { // executor是一个执行函数
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 默认成功的值
    self.reason = 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(reason) { // 失败状态
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) { 
        reject(e);
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) { 
        return reject(new TypeError('循环引用了'))
    }
    let called; 
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try { 
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    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 { 
        resolve(x); 
    }
}
Promise.prototype.then = function (onFulfilled, onRjected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw err;
    }
    let self = this;
    let promise2; 
    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 = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    
                    reject(e);
                }
            })

        })
    }

    if (self.status === 'pending') {
        promise2 = new Promise(function (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 = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}

Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}
相关文章
相关标签/搜索