Promise的简单介绍

首先,阅读一下这两篇文章。

  1. 异步-阮一峰
  2. promise MDN

通过上面两篇文章简单了解了Promise后,再来思考一下

why promise?

假设我们有一个需求,执行一个ajax请求,当请求成功后,再次执行一个ajax请求,代码实现可以这样写(以jquery为例)

$.get(url1,function(data){
    console.log('第一个ajax执行成功')
    $.get(url2,function(data){
        console.log('第二个ajax执行成功')
    })
})
复制代码

当我们需要在回调里执行第二次异步操作时,可以这样写,那如果我们的需求是需要连续十次执行,每次异步操作的执行都是在上一个异步操作的回调中执行,难道还要层层嵌套的写吗?当然不是,这样写的话会让代码非常不优雅,并且耦合度很大。 到这里,就轮到今天的主角-Promise登场了! 参考阮一峰大神的文章,可以看到Promise的大概实现如下

fn().then(fn1).then(fn2) //fn执行完后执行fn1,fn1执行完后执行fn2
复制代码

把上面提到的需求带进来,那就可以使用链式操作来完成我们的需求,可以看出,promise能够将异步的代码用同步的方式表示出来

Promise的简单示例我们可以参考MDN的简单示例

var myFirstPromise = new Promise(function(resolve, reject){
    //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
    //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
    setTimeout(function(){
        resolve("成功!"); //代码正常执行!
    }, 250);
});

myFirstPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(...)方法传入的值.
    //successMessage参数不一定非要是字符串类型,这里只是举个例子
    console.log("Yay! " + successMessage);
});
复制代码

接下来,我们自己动手写一个简单的Promise来对其原理有一个初步的认识。

何从下手?从使用开始

编写代码之前,先写一个简单的promise的执行语句

fn().then(fn1).then(fn2)
复制代码

参考官方文档,我们知道,then是promise对象的方法,这句代码能够链式调用,就说明fn最后返回的是一个promise对象,有头绪了!继续完善我们的代码

var p = new Promise() //创建一个promise对象,作为fn的返回

function fn() {
    console.log(1)
    setTimeout(function(){
        p.resolve("成功!")   //代码正常执行!这里执行异步回调
    },1000)
    return p
}

fn().then(fn1).then(fn2)
复制代码

then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法,这里我们假设每次异步都是执行成功的,暂时只传fulfilled一个函数作为参数,接下来,完善Promise对象,这里我们把Promise对象的onfulfilled 方法的属性名设为resolve,onrejected 方法的属性名设为reject,完善Promise

function Promise(){

}

Promise.prototype = {
    then: function(onfulfilled, onrejected){
        //then方法包含的两个方法参数
        this.resolve = onfulfilled
        this.reject = onrejected
        return this  //then也可以链式调用,所以执行完成后返回promise实例对象
    },
    resolve: function(result){
        // 回调成功执行的方法
    },
    reject: function(result){
        // 回调失败执行的方法
    }
}

var p = new Promise() //创建一个promise对象,作为fn的返回

function fn() {
    console.log(1)
    setTimeout(function(){
        p.resolve("成功!")   //代码正常执行!这里执行异步回调
    },1000)
    return p
}

fn().then(fn1).then(fn2)
复制代码

等等,貌似哪里不对?当我们语句中包含了两个then语句后,那就需要两个function放到任务队列中等待执行啊,如果这样写,后面的then的function岂不是会覆盖前面一个then的function吗?思考一下,我们可以把待执行的函数放在数组里顺序执行呀!继续修改代码

function Promise(){
    this.callbacks = []
}

Promise.prototype = {
    then: function(onfulfilled, onrejected){
        this.callbacks.push({
            resolve: onfulfilled,
            reject: onrejected
        })
        return this  //then也可以链式调用,所以执行完成后返回promise实例对象
    },
    resolve: function(result){
        // 回调成功执行的方法
        var callbackObj = this.callbacks.shift()
        callbackObj['resolve'](result)
    },
    reject: function(result){
        // 回调失败执行的方法
        var callbackObj = this.callbacks.shift()
        callbackObj['reject'](result)
    }
}
var p = new Promise()

function fn() {
    console.log(1)
    setTimeout(function(){
        p.resolve("成功!")   //代码正常执行!这里执行异步回调
    },1000)
    return p
}

fn().then(fn1).then(fn2)
复制代码

修改完成,看看还有哪里可以优化的地方?咦?resolve和reject的函数执行长得好像啊,是不是可以提取出公共部分来执行呢?继续修改,顺便完善一下fn1和fn2函数

function Promise(){
    this.callbacks = []
}

Promise.prototype = {
    then: function(onfulfilled, onrejected){
        this.callbacks.push({
            resolve: onfulfilled,
            reject: onrejected
        })
        return this  //then也可以链式调用,所以执行完成后返回promise实例对象
    },
    resolve: function(result){
        // 回调成功执行的方法
        this.complete('resolve', result)
    },
    reject: function(result){
        // 回调失败执行的方法
        this.complete('reject', result)
    },
    complete: function(type, result){
        var callbackObj = this.callbacks.shift()
        callbackObj[type](result)
    }
}
var p = new Promise()

function fn() {
    console.log('我是立即执行语句~')
    setTimeout(function(){
        p.resolve("成功!")   //代码正常执行!这里执行异步回调
    },1000)
    return p
}

function fn1(result) {
    console.log('fn1', result)
    setTimeout(function() {
        p.resolve('data2')
    }, 2000)
}

function fn2(result) {
    console.log('fn2', result)
}

fn().then(fn1).then(fn2)
复制代码

初步完成,把代码放到html中执行,效果如下

是不是还差了点儿什么?

再回头看看MDN文档,我们发现Promise的原型上有两个方法,then和catch,then我们已经实现了,接下来再实现一个简单的catch。回到我们的代码,当我们执行fn失败时,可以通过fn().then(successFunction,errorFunction)的形式来执行错误处理函数errorFunction,但是到这一步已经出错了,正常的逻辑是应该直接结束后续的回调,跳出这个流程,而我们目前完成的代码貌似还是会继续往后执行。。。再改改!

function Promise(){
    this.callbacks = []
    this.oncatch = null  //定义一个oncatch属性获取错误处理方法
}

Promise.prototype = {
    then: function(onfulfilled, onrejected){
        this.callbacks.push({
            resolve: onfulfilled,
            reject: onrejected
        })
        return this  //then也可以链式调用,所以执行完成后返回promise实例对象
    },
    resolve: function(result){
        // 回调成功执行的方法
        this.complete('resolve', result)
    },
    reject: function(result){
        // 回调失败执行的方法
        this.complete('reject', result)
    },
    complete: function(type, result){
        // 此处增加一个错误处理判断
        if (type === 'reject' && this.oncatch) {
            this.callbacks = []
            this.oncatch(result)
        } else if (this.callbacks[0]) {
            var callbackObj = this.callbacks.shift()
            if(callbackObj[type]) callbackObj[type](result)
        }
    },
    catch: function(onfail){
        this.oncatch = onfail
        return this
    }
}
var p = new Promise()

function fn() {
    console.log('我是立即执行语句~')
    setTimeout(function(){
        p.reject("失败!")   //代码正常执行!这里执行异步回调
    },1000)
    return p
}

function fn1(result) {
    console.log('fn1', result)
    setTimeout(function() {
        p.resolve('data2')
    }, 2000)
}

function fn2(result) {
    console.log('fn2', result)
}

function errCatch(result) {  //定义错误处理函数
    console.log('err', result)
}

fn().then(fn1).then(fn2).catch(errCatch)
复制代码

至此,我们完成了一个简单的promise实现,来看看错误获取的效果

可以看到,fn1和fn2并没有执行!大功告成!

简单的Promise对象已经完成了,来实战操(zhuang)作(b)一波! 1.将Promise作为一个common.js规范模块来引用 Promise.js

function Promise() {
    this.callbacks = []
    this.oncatch = null
}

Promise.prototype = {
    then: function(onfulfilled, onrejected) {
        this.callbacks.push({
            resolve: onfulfilled,
            reject: onrejected
        })
        return this //then也可以链式调用,所以执行完成后返回promise实例对象
    },
    resolve: function(result) {
        // 回调成功执行的方法
        this.complete('resolve', result)
    },
    reject: function(result) {
        // 回调失败执行的方法
        this.complete('reject', result)
    },
    complete: function(type, result) {
        if (type === 'reject' && this.oncatch) {
            this.callbacks = []
            this.oncatch(result)
        } else if (this.callbacks[0]) {
            var callbackObj = this.callbacks.shift()
            if (callbackObj[type]) callbackObj[type](result)
        }
    },
    catch: function(onfail) {
        this.oncatch = onfail
        return this
    }
}

module.exports = Promise
复制代码

2.新建3个txt文件,内容随意,再新建一个test.js文件

var Promise = require('./Promise')
var fs = require('fs')

var p = new Promise

var str = ''

function readFile(path){
    fs.readFile(path, 'utf-8', function(err, str){
        if(err){
            p.reject(path)
        }else{
            p.resolve(str)
        }
    })
    return p
}

readFile('a.txt').then(function(line){
    str += line
    console.log('读取a...')
    readFile('b.txt')
}).then(function(line){
    str += line
    console.log('读取b...')
    readFile('c.txt')
}).then(function(line){
    str += line
    console.log('读取c...')
    p.resolve(str)
}).then(function(result){
    console.log(result)
}).catch(function(){
    console.log('err')
})
复制代码

目录结构如下

在node环境下运行test.js,效果如下

再把某个读取步骤的路径改为一个不存在的文件试试

至此,一个简单的promise实现完成,如有错误,欢迎指正!拜拜

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值