Обещание передовой асинхронной технологии

JavaScript

предисловие

Друзья, которые занимаются фронтендом, контактировали более-менее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Состояние несовместимо, и в спецификации промиса указано, что когда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:GitHub.com/Чен Помфрет…  

Студенты, которые заинтересованы в добавлении друзей к автору, отсканируйте приведенный ниже QR-код (1. Мой WeChat, 2. Публичная учетная запись WeChat, 3. Группа WeChat по обмену технологиями), и я хотел бы подружиться с вами, чтобы обсудить технологии и болтать о жизни!


использованная литература

promisesaplus.com/

liubin.org/обещания-тонкие…

nuggets.capable/post/684490…