前端异步技术之Promise

Posted ccylovehs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端异步技术之Promise相关的知识,希望对你有一定的参考价值。

前言

从事前端的朋友或多或少的接触过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状态的promise
var promise = new Promise(function(resolve, reject){
    //do sth
})
//fulfilled状态的promise
var promise = Promise.resolve(1).then(function resolve(value){console.log(value)});
// var promise = new Promise(function(resolve){resolve(1)})
//rejected状态的promise
var 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#catch
promise.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`毫秒后执行resolve
function 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`毫秒后执行resolve
function 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://promisesaplus.com/

http://liubin.org/promises-book

https://juejin.im/post/5ab20c58f265da23a228fe0f







以上是关于前端异步技术之Promise的主要内容,如果未能解决你的问题,请参考以下文章

4.2 前端开发日报——JS异步编程之Promise详解和使用总结

前端设计技巧——用 Promise 处理交互和异步

前端ES6异步编程技术——Promise使用

js异步之Promise源码分析

前端片段整理

前端面试,备考第 15 天 - 异步编程:Promise | Async/Await | 定时器 | 扩展