前端异步(async)解决方案(所有方案)(三)

简介: 前端异步(async)解决方案(所有方案)

(3).next()调用中的传参

参数值有注入的功能,可改变上一个yield的返回值,如

function* showNumbers() {
    var one = yield 1;
    var two = yield 2 * one;
    yield 3 * two;
}
var show = showNumbers();
show.next().value // 1
show.next().value // NaN
show.next(2).value // 6

第一次调用next之后返回值one为1,但在第二次调用next的时候one其实是undefined的,因为generator不会自动保存相应变量值,我们需要手动的指定,这时two值为NaN,在第三次调用next的时候执行到yield 3 * two,通过传参将上次yield返回值two设为2,得到结果


另一个栗子:


由于ajax请求涉及到网络,不好处理,这里用了setTimeout模拟ajax的请求返回,按顺序进行,并传递每次返回的数据

var urls = ['url1', 'url2', 'url3'];
function* request(urls) {
    var data;
    for (var i = 0, j = urls.length; i < j; ++i) {
        data = yield req(urls[i], data);
    }
}
var r = request(urls);
r.next();
function log(url, data, cb) {
    setTimeout(function() {
        cb(url);
    }, 1000);
}
function req(url, data) {
    var p = new Promise(function(resolve, reject) {
        log(url, data, function(rs) {
            if (!rs) {
                reject();
            } else {
                resolve(rs);
            }
        });
    });
    p.then(function(data) {
        console.log(data);
        r.next(data);
    }).catch(function() {
    });
}

达到了按顺序请求三个地址的效果,初始直接r.next()无参数,后续通过r.next(data)将data数据传入

20200801150220845.png

注意代码的第16行,这里参数用了url变量,是为了和data数据做对比


因为初始next()没有参数,若是直接将url换成data的话,就会因为promise对象的数据判断 !rs == undefined 而reject


所以将第16行换成 cb(data || url);


通过模拟的ajax输出,可了解到next的传参值,第一次在log输出的是 url = 'url1'值,后续将data = 'url1'传入req请求,在log中输出 data = 'url1'值


(4).for...of循环代替.next()

除了使用.next()方法遍历迭代器对象外,通过ES6提供的新循环方式for…of也可遍历,但与next不同的是,它会忽略return返回的值,如

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}
var show = showNumbers();
for (var n of show) {
    console.log(n) // 1 2
}

此外,处理for…of循环,具有调用迭代器接口的方法方式也可遍历生成器函数,如扩展运算符…的使用

function* showNumbers() {
    yield 1;
    yield 2;
    return 3;
}
var show = showNumbers();
[...show] // [1, 2, length: 2]

更多使用可以参考:MDN - Generator

3.async await (重点)

es7新增的 async函数

可以更舒适地与promise协同工作,它叫做async/await,它是非常的容易理解和使用。

(1).格式

async function aa(){
        await '任务1'
        await '任务2'
}

async:

让我们先从async关键字说起,它被放置在一个函数前面。就像下面这样:

async function timeout() {
  return 'hello world';
}

函数前面的async一词意味着一个简单的事情:这个函数总是返回一个promise,如果代码中有return <非promise>语句,JavaScript会自动把返回的这个value值包装成promise的resolved值。


例如,上面的代码返回resolved值为1的promise,我们可以测试一下:

async function f() {
    return 1
}
f().then(alert) // 弹出1

我们也可以显式的返回一个promise,这个将会是同样的结果

async function f() {
    return Promise.resolve(1)
}
f().then(alert) // 弹出1

所以,async确保了函数返回一个promise,即使其中包含非promise,这样都不需要你来书写繁杂的Promise,够简单了吧?但是不仅仅只是如此,还有另一个关键词await,只能在async函数里使用,同样,它也很cool。

await:

// 只能在async函数内部使用
let value = await promise

关键词await可以让JavaScript进行等待,直到一个promise执行并返回它的结果,JavaScript才会继续往下执行。

以下是一个promise在1s之后resolve的例子:

async function f() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve('done!'), 1000)
    })
    let result = await promise // 直到promise返回一个resolve值(*)
    alert(result) // 'done!' 
}
f()

函数执行到(await)行会‘暂停’,不再往下执行,当promise处理完成后重新恢复运行, resolve的值成了最终的result,所以上面的代码会在1s后输出'done!'


我们强调一下:await字面上使得JavaScript等待,直到promise处理完成,


然后将结果继续下去。这并不会花费任何的cpu资源,因为引擎能够同时做其他工作:执行其他脚本,处理事件等等。


这只是一个更优雅的得到promise值的语句,它比promise更加容易阅读和书写。


注意不:能在常规函数里使用await


如果我们试图在非async函数里使用await,就会出现一个语法错误:

function f() {
   let promise = Promise.resolve(1)
   let result = await promise // syntax error
}
//Uncaught SyntaxError: await is only valid in async function

如果我们忘记了在函数之前放置async,我们就会得到这样一个错误。如上所述,await只能在async函数中工作。

就以前面几个案例可能还看不出async/await 的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?

async function testResult() {
    let first = await doubleAfter2seconds(30);
    let second = await doubleAfter2seconds(50);
    let third = await doubleAfter2seconds(30);
    console.log(first + second + third);
}

6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。


再来一个看看:先来个问题


readFile('./01-Promise.js') 运行结果是Promise, 但是我们使用 async await之后, 它的结果是具体的数据了?


用到了Node.js里的fs模块,fs模块是文件模块,可以操作文件,readFile()是读一个文件,不了解的乐意看Node.js官方文档

const fs = require('fs')//导入fs模块
const readFile = (filename) =>{
  return new Promise((resolve,reject)=>{
    fs.readFile(filename,(err,data)=>{
      resolve(data.toString())
    })
  })
}
const asyncFn = async() => {
   //const f0 = eadFile('./01-Promise.js') //类似{value: '文件内容', done: false}
  const f1 = await readFile('./01-Promise.js') //文件内容
  //const f1 =  readFile('./01-Promise.js').then(data=>data)
  const f2 = await readFile('./02-generator.js') //文件内容
  console.log( f1 )
  console.log( f2 )
}
asyncFn()

readFile()定义了一个Promise方法读取文件,这里有个坑,我们现在是在里面返回出数据了的,要知道这里面有3层函数,如果不用new Promise这个方法,大家可以试试用常规方法能不能返回数据,先透个底拿不到,大家可以试试。


asyncFn()输出了文件内容,在const f1 = eadFile('./01-Promise.js')这一句这一句会打印出出一个Promise{'文件内容'},有点类似前面的generator函数输出的{value: '', done: false},只不过省略了done,大家知道,我们读文件,肯定是要里面的内容的,如果输出 Promise{'文件内容'} ,我们是不好取出内容的,但是await很好的帮我们解决了这个问题,前面加上await直接输出了文件内容。


所以:这个问题可以有个小总结


1.async函数使用了generator函数的语法糖 , 它直接生成对象 {value: '',done:false} await 直接将value提取出来了


2. 通过Promise + async,我们可以把多层函数嵌套(异步执行)的里层函数得到的数据 返回出来


关于async/await总结


放在一个函数前的async有两个作用:


使函数总是返回一个promise

允许在这其中使用await

promise前面的await关键字能够使JavaScript等待,直到promise处理结束。然后:


如果它是一个错误,异常就产生了,就像在那个地方调用了throw error一样。

否则,它会返回一个结果,我们可以将它分配给一个值

他们一起提供了一个很好的框架来编写易于读写的异步代码。


有了async/await,我们很少需要写promise.then/catch,但是我们仍然不应该忘记它们是基于promise的,因为有些时候(例如在最外面的范围内)我们不得不使用这些方法。Promise.all也是一个非常棒的东西,它能够同时等待很多任务。


4.node.js nextTick setImmidate


nextTick vs setImmediate


轮询:


nodejs中是事件驱动的,有一个循环线程一直从事件队列中取任务执行或者

I/O的操作转给后台线程池来操作,把这个循环线程的每次执行的过程算是一次轮询.

2.setImmediate()的使用

即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个。

3.Process.nextTick()的使用

它和setImmediate()执行的顺序不一样,它是在事件轮询之前执行,为了防止I/O饥饿,所以有一个默认process.maxTickDepth=1000来限制事件队列的每次循环可执行的nextTick()事件的数目。


总结:


nextTick()的回调函数执行的优先级要高于setImmediate();

process.nextTick()属于idle观察者,setImmediate()属于check观察者.在每一轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者.

在具体实现上,process.nextTick()的回调函数保存在一个数组中,

setImmediate()的结果则是保存在链表中.

在行为上,process.nextTick()在每轮循环中会将数组中的回调函数全部执行完.

而setImmediate()在每轮循环中执行链表中的一个回调函数.


5.第三方库 async.js


async.js是一个第三方库,带有很多api


暴露了一个async对象,这个对象身上有很多的api(多任务执行),例如parallel,series

async.parallel(&#91;
function(callback){
callback(null,'任务1')
},
function(callback){
callback(null,'任务2')
},
],(err,data)=>{
console.log('data',data)
})


相关文章
|
2月前
|
前端开发 JavaScript API
前端:事件循环/异步
前端开发中的事件循环和异步处理是核心机制,用于管理任务执行、性能优化及响应用户操作,确保网页流畅运行。事件循环负责调度任务,而异步则通过回调、Promise等实现非阻塞操作。
|
2月前
|
前端开发 数据可视化 搜索推荐
深入剖析极态云优雅的前端框架设计方案(上)
最近在体验极态云,这款低代码软件开发产品,发现其前端框架设计方案很优雅很强大! 在接下来的学习过程中,我将持续输出自己对极态云前端框架设计方案的深入理解,包括具体的使用技巧、优势分析以及可能的应用场景等方面的内容,希望能为大家提供有价值的参考。
|
3月前
|
前端开发 JavaScript
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
乾坤qiankun(微前端)样式隔离解决方案--使用插件替换前缀
690 8
|
3月前
|
设计模式 前端开发 JavaScript
前端编程的异步解决方案有哪些?
本文首发于微信公众号“前端徐徐”,介绍了异步编程的背景和几种常见方案,包括回调、事件监听、发布订阅、Promise、Generator、async/await和响应式编程。每种方案都有详细的例子和优缺点分析,帮助开发者根据具体需求选择最合适的异步编程方式。
110 1
|
3月前
|
缓存 前端开发 UED
前端 8 种图片加载优化方案梳理
本文首发于微信公众号“前端徐徐”,详细探讨了现代网页设计中图片加载速度优化的重要性及方法。内容涵盖图片格式选择(如JPEG、PNG、WebP等)、图片压缩技术、响应式图片、延迟加载、CDN使用、缓存控制、图像裁剪与缩放、Base64编码等前端图片优化策略,旨在帮助开发者提升网页性能和用户体验。
592 0
|
4月前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
639 3
|
5月前
|
缓存 前端开发 JavaScript
前端性能优化方案
【8月更文挑战第15天】前端性能优化方案
55 2
|
4月前
|
编解码 前端开发 JavaScript
前端移动端适配方案
【9月更文挑战第8天】前端移动端适配方案
140 0
|
5月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
50 0
|
5月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
105 0

热门文章

最新文章