什么是javascript异步编程

06 Jun 2020

1. js有多线程吗

很多人认为setTimeout和setInterval就是JS中的多线程。setTimeout相当于启动一个线程,等待一段时间后执行函数,setInterval则是在另外的一个线程中,每隔一段时间执行函数。 实际上这种观点是错误的。 setTimeout实际上是把函数放入等待队列,等待主线程执行完毕后执行

示意图

那么异步Ajax是怎么回事呢?一个常用的开发实践就是发起一个异步的Ajax,界面显示一个进度条样式的Gif,说好的单线程呢?事实上异步>Ajax确实用了多线程,只是Ajax请求的Http连接部分由浏览器另外开了一个线程执行,执行完毕之后给JS引擎发送一个事件,这时候异步请>求的回调代码得以执行。它的执行流程是这样的:

Http请求的执行在另外一个线程中,由于这个线程并不会操作DOM树,所以是可以保证线程安全的。发起Ajax请求和回调函数中间是没有JS执行的,所以页面不会卡死。 作者:ma_fighting 链接:https://www.cnblogs.com/mafeng/p/6292534.html

另外浏览器对于事件的监听也是有单独的线程

2. promise是什么

我们通过new关键字和Promise构造器创建它的对象。这个构造器接受一个名为”executor function”的函数。这个函数应当接受两个函数参数。当异步任务成功时,第一个函数(resolve)将被调用,并返回一个值代表成功。当其失败时,第二个函数(reject)将被调用,并返回失败原因(失败原因通常是一个error对象)。

例子:

function myAsyncFunction(url) {
 return new Promise((resolve, reject) => {
   const xhr = new XMLHttpRequest()
   xhr.open("GET", url)
   xhr.onload = () => resolve(xhr.responseText)
   xhr.onerror = () => reject(xhr.statusText)
   xhr.send()
 });
}

作者:MDN docs 链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise

3. 利用setTimeout实现自己的简单promise(源码)

源码出处:后盾人教程

源码作者:后盾人教程-向军老师 官网:https://www.houdunren.com/

class HD{
    static PENDING = 'pending';
    static FUFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(executor){
        this.status = HD.PENDING;
        this.value = null;
        this.callbacks = [];
        try{
            executor(this.resolve.bind(this), this.reject.bind(this));
        }catch(error){
            this.reject(error);
        }
    }
    resolve(value){
        if(this.status === HD.PENDING){
            this.status = HD.FUFILLED;
            this.value = value;
            // this.callbacks.map(callback => {
            
            // })
            if(this.callbacks.length !== 0){
                setTimeout(() => {
                    (this.callbacks)[0](this.value);                
                });
            }
        }
    }
    reject(reason){
        if(this.status === HD.PENDING){
            this.status = HD.REJECTED;
            this.value = reason;
            if(this.callbacks.length !== 0){
                setTimeout(() => {
                    (this.callbacks)[1](this.value);
                });
            }
        }
    }
    then(onFufilled, onRejected){
        if(typeof onFufilled !== 'function'){
            onFufilled = () => {
                return this.value;
            }
        }
        if(typeof onRejected !== 'function'){
            onRejected = () => this.value;
        }
        let p =  new HD((resolve,reject) =>{
            if(this.status === HD.PENDING){
                // this.callbacks.push(
                //     {onFufilled, onRejected}
                // )
                this.callbacks.push(
                    value => {
                        this.parse(p,onFufilled(this.value),resolve,reject);
                    }, 
                    reason => {
                        this.parse(p,onRejected(this.value),resolve,reject)
                    }
                // console.log(this.callbacks.length)
                );
            }
            if(this.status === HD.FUFILLED){
                setTimeout(() => {
                    this.parse(p,onFufilled(this.value),resolve,reject);
                });
            }
            if(this.status === HD.REJECTED){
                setTimeout(() => {
                    this.parse(p,onRejected(this.value),resolve,reject)
                });
            }
        });
        return p;
    }
    parse(p,result,resolve,reject){
        if(p === result){
            // console.log(p);
            // try {
                throw new TypeError('Chaining cycle detected');
            // } catch (error) {
            //     reject(error);
            // }
        }
        try {
            if(result instanceof HD){
                result.then(resolve,reject);
            }else{
                resolve(result);
            }              
        } catch (error) {
            reject(error);
        }
    }
    static resolve(value){
        return new HD((resolve, reject) => {
            if(value instanceof HD){
                value.then(resolve,reject);
            }else{
                resolve(value);
            }
        });
    }
    static reject(value){
        return new HD((resolve, reject) => {
                reject(value);
        });
    }
    static all(promises){
        return new HD((resolve,reject) => {
            const values = []
            promises.forEach(promise => {
                promise.then(
                    value => {
                        values.push(value);
                        if(values.length === promises.length){
                            resolve(values);
                        }
                    },
                    reason =>{
                        reject(reason);
                    }
                );
            });
        });
    }
    static race(promises){
        return new HD((resolve,reject) => {
            promises
        });
    }
}
// // new Promise((resolve,reject)=>{
// resolve('resolved')
// reject('rejected')
// // })

注意:

this.resolve.bind(this)中bind(this)的用处是改变this指向,因为创建promise对象的时候会调用constructor,此时的this即是我们手写的promise对象,会将这个函数的caller指向本次创建的promise对象,如果不bind,resolve函数在当作参数传递时会丢失执行上下文,导致this指向是window而不是promise对象

箭头函数没有this问题,箭头函数可以捕获创建时的this,这里不得不用非匿名函数因为需要后续调用

更多内容可以参考Function.prototype.apply(),Function.prototype.call(),以及箭头函数

扩展阅读:

  1. Promise() constructor:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
  2. Function.prototype.apply():https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
  3. Function.prototype.call():https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
  4. Arrow function expressions:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arrow_functions
  5. 手写promise视频-b站:https://www.bilibili.com/video/BV137411e7KA?from=search&seid=3948419975474208834
  6. 前端面试系列-JavaScript-箭头函数(与普通函数的区别): https://blog.csdn.net/qq_39903567/article/details/115208019

如有侵权联系删除

创作不易,感谢支持

请选择支付方式
USD

比特币-打赏地址:

1DGiAzDacFRxewyos23C14cKcgD5LGZ5hK

狗币-打赏地址:

DRpHTcQXKcauPktjz9WMALST3Vnf5SviDs

以太坊-打赏地址:

0xd34447399c497337a61eccb29cc2ef3e0dad7d13

其他加密货币-打赏地址:

coming soon