ES6的callback promise async的用法

目录

前言

1callback的语义和用法

2promise的语义和用法

3async的语义和用法

前言

弄清楚这个问题之前,首先我们得搞清楚JS里面什么是同步,什么是异步,这两者有什么区别?

JS是单线程的,负责解释和执行JS的代码的线程只有一个,称之为主线程。单线程意味着前一个任务结束,才会执行后一个任务。其实实际上还有其他线程,例如工作线程。

同步:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。如果在函数A返回的时候,调用者就能够得到预期的结果,那么这个函数就是同步的。

异步:不进入主线程,而是进入任务队列的,通过任务队列通知主线程,某个异步任务可以执行。如果在函数返回的时候,调用者不能马上得到预期的结果,那么这个函数就是异步。例如常用Ajax请求。就是典型的异步函数。

 我们分成执行栈,消息队列,任务队列。

工作线程在异步操作完成后,将消息放到消息队列,主线程通过事件循环去取消息。

任务队列是一个事件的队列。所有任务都是在主线程上执行,形成一个执行栈。主线程代码执行完后,才会去执行任务队列中的事件,只要执行栈一清空,任务队列的第一事件就自动进入主线程。比如定时器,主线程要检查一下执行事件,只有到了规定的时间,才能返回主线程。

以下为同步代码

<script>
        // 同步  执行顺序符合代码的顺序

        console.log("我是第一个div的盒子");
        console.log("我是第二个div的盒子");
        console.log("我是第三个div的盒子");
        console.log("我是第四个div的盒子");
        console.log("我是第五个div的盒子");

</script>

以下为异步代码

<script>

    // 异步测试    setTimeout() 在指定的毫秒数后执行某些操作

    console.log("我是第一个div盒子");
    setTimeout(function(){
        console.log("我是第二个div盒子");
    },3000);
    console.log("我是第三个div盒子");
    
</script>

最典型的异步操作就是setTimeOut()和setTimeInterval() 他们可以控制JS的执行顺序。可以使用clearTimeOut()来清除定时器,在指定的毫秒数后,将定时任务处理函数添加到执行队列的队尾。

setInterval方法用于每隔多少时间执行某些操作,可以使用clearInterval来清除定时,按照指定的周期,将定时任务处理函数添加到执行队列的队尾。

1callback的语义和用法

我们来回顾几个经典的回调函数代码。

以下是Geogle的解释, 非常清晰简明

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

        $.get('url', function(data) {
            alert("success");
        })
        $('#btnDelete').click(function() {
            alert("success");
        })
        var fruits = ["A", "B", "C"];
        var arr = []
        fruits.forEach(function(element) {
            element += "zz"
            arr.push(element)
        })

        function getNodes(params, callback) {
            var list = JSON.stringify(params)
            typeof(callback) === 'function' && callback(list);
        }
        getNodes('[1,2,3]', function(nodes) {
            //拿到nodes后用它去做一些其他操作
        })

以上几个分别是调用Ajax 点击事件的回调函数 数组中遍历 的异步回调函数。最后一个就是普通的同步回调的例子。所以回调与同步和异步并没有什么区别。回调只是一种实现方式,既可以同步回调,也可以异步回调,还可以有事件处理回调和延迟函数回调。

2promise的语义和用法

网上讲的有很多,但自己的思路还是比较乱,所以在此稍微总结一下,并且表示自己的看法

Promise通常用于更容易处理异步操作或阻塞代码,比如文件操作,API操作,IO调用。这些异步操作的启动发生在执行函数中,如果异步操作成功,则通过Promise的创建者调用resolve函数返回预期结果。同样,如果调用失败,则通过reject函数传递错误具体信息。

首先我们来搞清楚Promise中的状态,promise只有三种状态pending,fulfilled,rejected后面两种称为settled resolved是指promise已经settled或者已经使用另一个promise(B)来resolve了,此时promise的状态由B来决定。

一旦状态改变就是从pending到resolved或者从pending到rejected,只要这两种情况发生,状态就凝固了。如果一切正常,则调用resolve 否则调用reject。

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

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

对于已经实例化的promise对象可以调用promise.then对象,传递方法作为回调。最常规的写法是

promise.then(onFulfilled).catch(onRejected)

function ajax(URL) {
	return new Promise(function (resolve, reject) {
		var req = new XMLHttpRequest(); 
		req.open('GET', URL, true);
		req.onload = function () {
		if (req.status === 200) { 
				resolve(req.responseText);
			} else {
				reject(new Error(req.statusText));
			} 
		};
		req.onerror = function () {
			reject(new Error(req.statusText));
		};
		req.send(); 
	});
}
var URL = "/try/ajax/testpromise.php"; 
ajax(URL).then(function onFulfilled(value){
	document.write('内容是:' + value); 
}).catch(function onRejected(error){
	document.write('错误:' + error); 
});

上述代码很好理解,就是发起Ajax请求时,如果请求成功就是reject如果请求失败那么就抛出异常,并且在函数类声明这个异常。

function greet(){
var promise = new Promise(function(resolve,reject){
    var greet = "hello  world";
    resolve(greet);
});
return promise;
}
var p = greet().then(v=>{
console.log(v);
})

console.log(p);
//Promise { <pending> }
//hello  world

-----------------------
greet().then(v=>{
    console.log(v+1);
    return v;
})
.then(v=>{
    console.log(v+2);
    return v;
})
.then(v=>{
    console.log(v+3);
})
//hello  world1
//hello  world2
//hello  world3

上述代码的含义就是将promise作为函数的返回值,然后调用。先输出promise,并且其处于pending状态,并且promise执行then之后还是Promise,所以就根据这一特性,不断的链式调用回调函数。

all方法提供了并行执行异步操作的能力,在all中所有异步操作结束后才执行回调

function p1(){
    var promise1 = new Promise(function(resolve,reject){
        console.log("p1的第一条输出语句");
        console.log("p1的第二条输出语句");
        resolve("p1完成");
    })
    return promise1;
}

function p2(){
    var promise2 = new Promise(function(resolve,reject){
        console.log("p2的第一条输出语句");
        setTimeout(()=>{console.log("p2的第二条输出语句");resolve("p2完成")},2000);

    })
    return promise2;
}

function p3(){
    var promise3 = new Promise(function(resolve,reject){
        console.log("p3的第一条输出语句");
        console.log("p3的第二条输出语句");
        resolve("p3完成")
    });
    return  promise3;
}

Promise.all([p1(),p2(),p3()]).then(function(data){
    console.log(data);
})

p1的第一条输出语句
p1的第二条输出语句
p2的第一条输出语句
p3的第一条输出语句
p3的第二条输出语句
p2的第二条输出语句
[ 'p1完成', 'p2完成', 'p3完成' ]

等到定时器里面的执行完毕后,后面的.then方法才会执行

race的用法,在all中的回调函数,等到第一个Promise改变状态就开始执行回调函数

p1的第一条输出语句
p1的第二条输出语句
p2的第一条输出语句
p3的第一条输出语句
p3的第二条输出语句
p1完成
p2的第二条输出语句 

说明当执行then方法时,只有第一个promise的状态改变了。

最后再补充一个小的知识,不要在promise后面执行一些依赖promise改变才能执行的代码,就比如我想在resolve状态时,得到一些值,那么为了获得这些值,我可以将其延迟输出就可得到。

var i

var promise = new Promise(function(resolve, reject) {
    resolve("hello");
})

promise.then(data => {
    i = data;

})
console.log(i);//undefined 因为方法还没执行完
setTimeout(()=>console.log(i),1000);//hello

3async的语义和用法

async函数返回的是一个Promise对象,如果用return的话,它会把这个值通过resolve封装成一个promise对象 ,相当于new Promise(resolve =>resolve(x))

async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);//Promise { 'hello async' }

而await就是等待一个async的返回值,可以是promise对象或者其他值,因此await后面实际是可以接普通函数调用或者直接量的。

function getSomething() {
    return "something";
}

async function testAsync() {
    return Promise.resolve("hello async");
}

async function test() {
    const v1 = await getSomething();
    const v2 = await testAsync();
    console.log(v1, v2);
}

test();//something hello async

对await来说

如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

以下代码为不用async实现的一个异步功能,最主要是promise采用了组成链看上去是比较复杂的

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1.530s

以下代码为使用async ,给我的感觉几乎就是同步代码,但实际上是异步的。因此可以很明显的感受到promise的参数传递太麻烦,因此我们引入了async/await

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清辞-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值