继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

前端异步技术之Promise

胡说叔叔
关注TA
已关注
手记 324
粉丝 129
获赞 581

前言

从事前端的朋友或多或少的接触过Promise,当代码中回调函数层级过多你就会发现Promise异步编程的魅力,相信此文一定能帮你排忧解惑!

Promise概念

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一
或许是笔者理解能力有限,对官方术语怎么也感受不到亲切,下面我来用通俗易懂的语言解释下:
Promise是一个包含三种状态的对象(pending、fulfilled、rejected),可以链式的处理异步请求(then方法)并能很好地处理异常问题,是解决回调地狱的良好方案之一。
回调函数处理多层异步示例

复制代码

$.ajax({
    url: url1,
    success: function(rsp){
        $.ajax({
           url: url2,
           success: function(rsp){
               $.ajax({
                  url: url3,
                  success: function(rsp){                      //do sth                  },
                  error: function(error){
                  }
              });
           },
           error: function(error){
           }
       });
    },
    error: function(error){
    }
});

复制代码

将promise封装在$.ajax中

复制代码

$.ajax = function(config){    return new Promise(function(resolve, reject){        //1省略...
        xmlhttp.onreadystatechange = function(){            if(xmlhttp.status==200){
                resolve(rspData);
            }else{
                reject(xmlhttp.statusText);
            }
        };        //2省略...    })
}
$.ajax({url: url1}).then(function(val){    return $.ajax({url: val.url})
}).then(function(val){    return $.ajax({url: val.url})
}).catch(function(err){
    console.log(err);
}}

复制代码

封装好的Promise处理异步可读性可维护性以及代码美观度不言而喻

Promise API

'new' Promise

复制代码

//pending状态的promisevar promise = new Promise(function(resolve, reject){    //do sth})//fulfilled状态的promisevar promise = Promise.resolve(1).then(function resolve(value){console.log(value)});// var promise = new Promise(function(resolve){resolve(1)})//rejected状态的promisevar promise = Promise.reject(new Error('error')).catch(function(error){console.error(error)});// var promise = new Promise(function(resolve,reject){resolve(new Error('error'))})

复制代码

Promise.prototype.then

Promise#then
promise.then(onFulfilled, onRejected)
返回一个新的promise。这里经常会有一个疑问:为什么不返回原来的promise,个人是这样认为的,若返回同一个promise则状态不一致,promise规范说明当pending至fulfilled/rejected时状态确定后不能再改变。

Promise.prototype.catch

Promise#catchpromise.catch(function(error){    throw new Error(error);
})
注意:IE8及以下版本会出现 identifier not found 的语法错误,可将点标记法改为中括号标记法
promise['catch'](function(error){    throw new Error(error);
})
rejected状态的promise抛出异常
相当于
promise.then(undefined, onRejected)
then & catch 结合示例

复制代码

promise.then(function f1(value){    //do sth 1}).then(function f2(value){    //do sth 2}).then(function f3(value){    //do sth 3}).catch(function(error){
    console.log(error);
})

复制代码

Promise.prototype.finally

promise.finally(onFinally)
返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行

Promise.all

promise.all([promise1, promise2, promise3]).then(resolve);
示例

复制代码

// `delay`毫秒后执行resolvefunction timerPromisefy(delay) {    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}var startDate = Date.now();// 所有promise变为resolve后程序退出Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms');    // 约128ms
    console.log(values);    // [1,32,64,128]});

复制代码

在接收到所有的对象promise都变为 FulFilled 返回一个resolve(array),或者 某一个promise对象变成Rejected 状态返回resolve(err)
传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。

Promise.race

promise.race([promise1, promise2]).then(resolve, reject)
示例

复制代码

// `delay`毫秒后执行resolvefunction timerPromisefy(delay) {    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}// 任何一个promise变为resolve或reject 的话程序就停止运行Promise.race([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (value) {
    console.log(value);    // => 1});

复制代码

只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

Promise polyfill & Test

promise-polyfill.js

学习完Promise后,必定要重写Promise,后续遇到浏览器环境不支持也可有的放矢
代码如下

复制代码

/**
 * @author chenchangyuan
 * @date 2019-02-23
 * */function Promise(executor){    if(typeof executor !== 'function'){        throw new Error('executor is not a function');
    }    var self = this;
    self.state = 'pending';//pending fulfilled rejected
    self.value = null;
    self.reason = null;
    self.callbackResolveFn = [];
    self.callbackRejectFn = [];    function resolve(value){        if(self.state === 'pending'){
            self.state = 'fulfilled';
            self.value = value;
            self.callbackResolveFn.forEach(function(fn){
                fn();
            });
        }
    }    function reject(reason){        if(self.state === 'pending'){
            self.state = 'rejected';
            self.reason = reason;
            self.callbackRejectFn.forEach(function(fn){
                fn();
            });
        }
    }    try{
        executor(resolve, reject);
    }catch(err){
        reject(err);
    }
}//回溯函数function resolvePromise(promise, x, resolve, reject){    if(promise === x) return reject(new TypeError('循环引用'));    var flag = false;    if(x !== null && (typeof x === 'object' || typeof x === 'function')){        try{            var then = x.then;            if(typeof then === 'function'){
                then.call(x, function(val){                    if(flag) return;
                    flag = true;
                    resolvePromise(promise, val, resolve, reject);
                }, function(err){                    if(flag) return;
                    flag = true;
                    reject(err);
                });
            }else{
                resolve(x);
            }
        } catch(err){            if(flag) return;
            flag = true;
            reject(err);
        }

    }else{
        resolve(x);
    }
}//返回一个新的promise(pending:push(fn),fulfilled:resolve(val),rejected:reject(reason))Promise.prototype.then = function(onFulfilled, onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){        return value;
    };
    onRejected = typeof onRejected === 'function' ? onRejected : function(err){        throw new Error(err);
    };    var self = this,
        promise2;    if(self.state === 'fulfilled'){
        promise2 = new Promise(function(resolve, reject){
            setTimeout(function(){                try{                    //将x处理成一个原始值
                    var x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e){
                    reject(e);
                }
            })
        })
    }    if(self.state === 'rejected'){
        promise2 = new Promise(function(resolve, reject){
            setTimeout(function(){                try{                    //将x处理成一个原始值
                    var x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(e){
                    reject(e);
                }
            })
        })
    }    if(self.state === 'pending'){
        promise2 = new Promise(function(resolve, reject){
            self.callbackResolveFn.push(function(){
                setTimeout(function(){                    try{                        //将x处理成一个原始值
                        var x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e){
                        reject(e);
                    }
                })
            });
            self.callbackRejectFn.push(function(){
                setTimeout(function(){                    try{                        //将x处理成一个原始值
                        var x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e){
                        reject(e);
                    }
                })
            });
        })
    }    return promise2;
}
Promise.prototype['catch']= function (callback) {    return this.then(undefined, callback)
}
Promise.all = function (promises) {    return new Promise(function (resolve, reject) {
        let arr = [];
        let i = 0;        function processData(index, y) {
            arr[index] = y;            if (++i === promises.length) {
                resolve(arr);
            }
        }        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}
Promise.race = function (promises) {    return new Promise(function (resolve, reject) {        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    });
}
Promise.resolve = function(value){    return new Promise(function(resolve,reject){
        resolve(value);
    });
}
Promise.reject = function(reason){    return new Promise(function(resolve,reject){
        reject(reason);
    });
}
Promise.defer = Promise.deferred = function () {    var d = {};
    d.promise = new Promise(function (resolve, reject) {
        d.resolve = resolve;
        d.reject = reject;
    });    return d
}
module.exports = Promise;

复制代码

promise-aplus-tests

由于是参(抄)考(袭)前辈的polyfill,自己编码测试时出现了两处错误,ES6 Promise 规范的2.3.1和2.3.4

2.3.1

2.3.4

经过改正测试成功

后记

你们的支持是我最大的动力,熬夜码字不易,如果此文对你有帮助,请不吝star--->https://github.com/chenchangyuan/promise

有兴趣加笔者好友的同学请扫描下方二维码(1.本人微信,2.微信公众号,3.技术交流微信群),愿与您成为好友共同探讨技术,畅聊生活!

原文出处:https://www.cnblogs.com/ccylovehs/p/10425199.html 

作者:蟹丸 

打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP

热门评论

你这排版希望再优化一下?

查看全部评论